Docs: Pangea v2: factory v2 launches, the full reference, and v1

Factory v2 made every launch from 2026-10-27 until 2026-11-02, and those coins keep its rules. This tab is how a v2 launch works, and the full reference v3 builds on: the chains a creator picks, the opening market cap, degen mode, coin tokenomics, the fees, bridging, routes and the APIs. Where v3 differs, the v3 tab says so. The coins launched before factory v2, from the v1 factory, are at the end: Earlier (v1).

  • Every token launches on up to five chains at once, one pool on each. No bonding curve.
  • Price is automatically arbitraged using LayerZero Omnichain Fungible Tokens.
  • Liquidity is locked on every chain: no contract function or program instruction withdraws it.
View as Markdown
Sheet AThe shape
A.1

How it works

A.1

Factory v2. Factory v2 made every launch from 2026-10-27 until 2026-11-02, and this tab describes it; every launch since is made by factory v3: Pangea v3 → A coin launched before factory v2 keeps the rules it launched under: V1 (earlier launches) →

By default a launch deploys the same token on all five chains at once. Each chain gets 1B tokens in its liquidity pool with degen mode off, or its equal share of 1B in all (200M on five chains) in degen mode, which is on by default on the launch form; all five pools open at the same US dollar price.

Launch on selected chains. A creator can instead pick the chains: any two to five of the five, at least one of them an EVM chain. With degen mode off, each chosen chain still gets its own 1B, so a launch on three chains is 3B in all. The token never exists on the chains left out.

Opening market cap. The creator picks the market cap the whole coin opens at, all of its chains together: $3,000 to $500,000 a chain times the chains (in degen mode $3,000 to $500,000 in all). Every chain opens at the same price, with its share. On factory v3 there is no locked supply: degen mode is normal mode, every chain minting its own 1B and pooling all of it, opening at $600 a chain ($3,000 for a coin on five chains). The least a v3 launch may open at is $600 a chain, which the platform may set between $600 and $3,000 a chain; standard mode still opens at $3,000 a chain unless the creator picks another cap. Opening market cap →

Degen mode. One switch on the launch form, on by default, makes the coin 1B in all: each chain mints and pairs an equal share, 200M (20%) on each of five chains. Turned off, each chain gets its own 1B. Degen mode →

Every token a chain mints goes into its liquidity pool from the start. There is no team allocation, treasury or vesting. Each pool starts with tokens only and collects the quote asset as people buy.

Each chain operates independently, and one thing keeps the prices together: arbitrage, which is open to anyone. Buy on a cheaper chain and sell on a more expensive one.

A launch can also set a different trading fee on each chain, send its fees to a fee receiver and spend the creator’s share through coin tokenomics. From a program, the same launch goes through the Launch API.

A.2

The five pools

A.2

Each chain gets 1 billion tokens in its liquidity pool (in degen mode, its equal share of the 1B), held in a concentrated liquidity position. As the price rises, the pool sells tokens and collects the quote asset.

ChainVenueQuote
EthereumUniswap v4ETH
BaseUniswap v4ETH
Robinhood ChainUniswap v4ETH
BSCUniswap v4BNB
SolanaMeteora DAMM v2SOL

Each pool pays sellers from its own reserves. A chain with fewer buyers may have less quote asset available, regardless of how much liquidity exists elsewhere.

Opening market cap

The creator picks the market cap the whole coin opens at, all of its chains together: $3,000 to $500,000 a chain times the chains, in whole dollars (a total that does not divide by the chains is rounded down to a whole dollar a chain), and $3,000 a chain when left alone. The price per token is that market cap over the whole supply on every chain, and every chain opens at that one price with its share of the supply, so a $100,000 launch on four chains opens at $25,000 on each. It is signed into the launch and the contracts refuse any other, so nobody can open below $3,000 a chain.

In degen mode the market cap is likewise the whole coin's, from $3,000 to $500,000 in all: every chain reports the whole 1B, so terminals show that market cap on each chain, and each chain's pool holds its share of it. A higher opening market cap is a deeper pool: the same buy moves the price less. On factory v3 there is no locked supply: degen mode is normal mode, every chain minting its own 1B and pooling all of it, opening at $600 a chain ($3,000 for a coin on five chains). The least a v3 launch may open at is $600 a chain, which the platform may set between $600 and $3,000 a chain; standard mode still opens at $3,000 a chain unless the creator picks another cap.

A.3

Five contracts, one token

A.3

Each chain has its own token contract and address, so buyers choose which chain to trade on.

A token’s address is different on every chain (unless its creator chose “Same contract address on all EVM chains”, which puts it at one address on the four EVM chains), and every one of them ends in 6ea: the last three hex digits on the four EVM chains, the last three characters of the mint on Solana. The token’s page lists every one.

Each chain has 1 billion tokens in its own liquidity pool, so 5 billion across the five chains.

One launch, five contracts
Ethereum1BIn its pool, own address
Base1BIn its pool, own address
Robinhood1BIn its pool, own address
BNB Chain1BIn its pool, own address
Solana1BIn its pool, own address

Each contract reports its own chain’s 1B supply.

A launch on selected chains has a contract on each of its chains only. In degen mode every contract reports the coin’s whole 1B instead. Degen mode →

A.4

Degen mode

A.4

Degen mode is one switch on the launch form, on by default. The coin is 1B in all, not 1B a chain. Each of the launch’s chains mints the same share of it and puts all of that share in its pool: 200M, 20% of the supply, on each of five chains. On fewer chains each share is larger: 500M each on two.

Five chainsStandardDegen mode
Total supply5B: 1B a chain1B
Minted and paired on each chain1B200M (20%)
What each chain’s contract reportsIts own 1BThe whole 1B
The rest of the supplyNoneIn Solana’s bridge reserve

Every chain’s contract reports a total supply of 1B, so a terminal on any chain shows the market cap of the whole coin. Circulating is that same 1B, everything minted and paired across the chains less anything burned: the bridge reserve below backs the other chains and is not extra supply, so it is never taken off circulating, and the market cap is the price times 1B.

The bridge reserve on Solana

On a launch with Solana, Solana’s mint holds the full 1B: Solana’s own share in its pool, and everything else in a bridge reserve owned by Pangea’s launch program. It can only be burned: nobody can take tokens out of it or spend them. It moves only with bridging. When tokens are bridged to Solana, the same number is burned from the bridge reserve, and when tokens leave Solana, the same number is added back to it, so Solana’s supply stays 1B.

A degen mode launch without Solana has no bridge reserve: its chains mint their shares, and every one of them reports 1B.

The opening market cap is chosen the same way as for any launch and is the whole coin's, every chain together; every chain opens at the same price, with its share. Opening market cap →

A coin launched in degen mode carries the degen mode mark on the explorer and on its coin page (hover or tap it to read “Degen mode”); a coin launched without it carries none. Its tokenomics shares are tags too: a small icon with the share, such as the flame with “30%” (burn) or the gift with “70% · USDT” (holder rewards); hover or tap one to read its name.
A.5

The market cap situation

A.5

Terminals and apps calculate market cap in different ways. A token’s full market cap is the sum of its market caps on all five chains.

The price is the same on every terminal and app. You can bridge your tokens, or buy on any of the five contracts, at the same dollar price for the same number of tokens.

To see a token’s complete market cap, use its page on this site: it is the most accurate source.

A coin launched in degen mode is the exception: every chain reports the whole 1B, so each chain’s market cap is already the whole coin’s.

The differences between platforms come from how Pangea manages supply on each chain to keep prices in line. That is what keeps the price gap between chains small, and what makes a single token across many chains possible. Every platform is consistent with itself, and many of them show the same figures.

The key point: the same token, at the same price, on every chain.

Sheet BThe money
B.1

Launch fee

B.1

You choose one of the launch’s chains to pay on and pay once, from your own wallet, in that chain’s native asset. That one payment covers the launch on all of its chains, so you need no funds on the others.

Pay onPaid inFrom
EthereumETHYour EVM wallet
BaseETHYour EVM wallet
Robinhood ChainETHYour EVM wallet
BSCBNBYour EVM wallet
SolanaSOLYour Solana wallet

What it pays for

  • Gas on the four EVM chains. The launch transaction on each of the launch’s EVM chains (Ethereum, BSC, Base, Robinhood Chain), priced at 8,250,000 gas each, 5,000 more on the chain you pay on, at that chain’s gas price when you ask for the quote (on Ethereum, plus the 0.05 gwei extra tip its launch may bid to land in the next block), plus the 1,000,000 wei opening buy (0.000000000001 ETH or BNB) each launch makes in its own pool.
  • Rent and fees on Solana. 0.0288 SOL, on a launch with Solana: the rent for the accounts the token needs there (its mint, its pool and position, its LayerZero store and escrow, its record in Pangea’s program) and the fees of the transactions that create them. In degen mode, 0.00394208 SOL more for the bridge reserve’s accounts.
  • Tokenomics, when you turn it on. 450,000 gas on each EVM chain to set up the coin’s vault there, and on a launch with Solana a “Solana tokenomics setup” line on the form: 0.01512 SOL, or 0.027 SOL when Solana’s split has a liquidity share.
  • A platform margin. 20% on top of that cost, for gas moving between the quote and the launch and for a chain that has to be retried.

So the amount is not fixed. It moves with gas prices on the four EVM chains and with the prices of ETH, BNB and SOL. It is quoted when you prepare the launch, and the launch page shows it in the asset you pay in, with its approximate US dollar value, before you sign. A quote is good for 10 minutes; once it runs out, you prepare again for a fresh one.

At today’s prices a launch typically costs about $8 to $10. For example, TEST4’s quote was $8.16: $6.80 of cost plus the 20% margin, paid as 0.0112 BNB. TEST3’s was $9.20, from $7.66 of cost.

Dev buys are off for now: the launch form does not offer one and the Launch API refuses one. Launch buys are the way to buy at launch (the V3 tab). When a dev buy is offered, it is optional. If you set one, it is your own money: it is paid on top of the fee in the same transaction, on the same chain, and shown separately. The fee then includes what the buy adds to the launch’s own cost, 150,000 gas on an EVM chain or one token account’s rent on Solana, so the platform pays no part of it.

The fee is held, not taken, until your launch lands. If the launch never happens it is refunded, or you can take it back yourself after 7 days.
B.2

Trading fee

B.2

Each launch sets a trading fee between 0.3% and 5%, once, at launch. No contract function or program instruction changes it afterwards.

By default every chain charges the same fee. A creator can instead pick a different fee on each chain, each between 0.3% and 5%: each chain's fee is signed into that chain's part of the launch and is just as fixed.

The platform takes 20% of the trading fee, with a floor of 0.2% of volume. The creator receives the rest.

Trading fee
1.00%
Platform
0.20%
(20%)
Creator
0.80%
(80%)

Percentages of volume. In brackets, each side's share of the trading fee itself.

Trading feePlatformCreator
0.3%0.2% (floor)0.1%
1%0.2%0.8%
5%1.0%4.0%
The minimum fee is 0.3% because Meteora keeps 20% of trading fees on Solana. At a 0.2% fee, only 0.16% of volume would remain, less than the platform’s share. This also means creators earn less on Solana: at a 1% trading fee they receive 0.6% there against 0.8% on Base.

80% of platform fees go to PANGEA buybacks.

PANGEA buybacks
80%
Platform
20%

Of platform fees.

Who receives the creator’s share. The creator wallets, unless the launch names a fee receiver or turns on tokenomics. Creator wallets and fee receiver → On Solana, the fee split is enforced by Pangea's splitter program.

Where the fee goes can change. Only the platform can move the creator’s share to another address. A coin launched before factory v2 works differently: V1 trading fee →

B.3

Factory v2 (superseded by v3)

B.3
Superseded. From 2026-11-02 launches were made by factory v3, on all five chains, and every new launch is now v4. This page is kept for coins launched by factory v2 between 2026-10-27 and 2026-11-02, whose rules do not change. What changed in v3 →

From 2026-10-27 until 2026-11-02, every launch on Ethereum, Base, BSC and Robinhood Chain came from factory v2. Solana was unchanged: its launches kept going through the same splitter program. Factory addresses →

What is new for a launch

  • The coin’s own tokenomics v2 vault can be named as its fee receiver. The factory checks it, and refuses a vault made for any other launch or creator.
  • A tokenomics v1 vault is accepted as the fee receiver only when every EVM chain of the launch is paired with its native coin.

The new fee splitter

Each EVM chain of a new launch has a FeeSplitterV2. The trading fee and the split between the creator and the platform are fixed for good. The fee receiver can be moved, in two steps: the platform proposes a new address, and the move takes effect only when that address accepts. The creator cannot move it alone.

Coins launched earlier keep the fee splitter they launched with. Their rules do not change, and they keep trading and bridging as before.

B.4

Creator wallets and fee receiver

B.4

Creator wallets. Every launch names two: one EVM address and one Solana address, because an EVM key cannot receive on Solana and a Solana key cannot receive on the EVM chains. They are the coin’s creator: they own its page, its socials and its dev buy, and they receive its share of the trading fees unless a fee receiver is set.

Fee receiver. Optional. It sends the trading fees to another address, such as a Safe or a partner, on the EVM chains, on Solana, or both. You stay the creator: the page, the socials and the dev buy are still yours, and only the fees go to the receiver. Left empty, the fees go to your creator wallet. Only the platform can change it later.

  • An address that is a contract on one of the launch’s chains is asked about first: tick that it is your own Safe or smart wallet and that it can send on what it is paid.
  • “Burn my creator fees” makes the fee receiver the burn address on every chain, for good. You stay the creator.
  • With tokenomics on, the fee receiver is not used: the fees go to the coin’s vault, which pays your creator share to your creator wallet. Coin tokenomics →

The connected wallet

The creator wallets start as the wallet you have connected. Switch wallets in your wallet app and a creator wallet field that came from the old wallet follows the new one; an address you typed yourself stays as it is. A quote prepared for the old wallet is dropped, since the new wallet cannot sign for it: prepare again. Nothing changes while a payment is under way.

Creator fees on your profile

  • A new launch’s creator fees show on its creator’s profile within a minute or two of launch, on every chain of the launch, Solana included, and the coin shows in the Created tab.
  • Anything not read yet is said on the page, such as fees pending on a chain, instead of being shown as zero.
  • When fees are paid out, the payout moves from unclaimed to claimed within about a minute.
  • “Claim now” pays a launch’s fees out at once, split exactly as the automatic payout does: you send the transaction and pay its gas.
B.5

Coin tokenomics

B.5

With “Spend my fee share automatically” on, the creator’s share of the trading fees goes to a vault made for that launch, not to the creator wallet. The vault spends it in the shares you set at launch. The split is fixed at launch.

BucketWhat the vault does with it
BurnBuys your coin and burns it.
Buy and hold (treasury)Buys your coin and sends it to your treasury wallet.
Add to liquidityBuys your coin and adds it to the pool, locked for good.
CreatorPaid to your creator wallet, in the chain’s coin.
Community walletPaid to a community wallet, in the chain’s coin.
  • The shares add up to 100%. The form offers presets (Balanced, All burn, Liquidity, Community) or your own split.
  • “A different split on each chain” sets each chain’s shares on their own. Otherwise every chain uses the same split.
  • It works on every chain of the launch, Solana included. Solana takes its own treasury and community wallets.
  • With tokenomics on, the fee receiver is not used, the fees cannot also be burned, and you cannot send your own chain’s launch yourself: the vault is the coin’s fee receiver.

When the vault runs

  • At most once every 15 minutes on each chain, at the market price. There is no reference price, and each buy’s price impact is capped at the pool’s own fee, so a large balance is spent over several runs.
  • A bucket waits until it holds enough to be worth a run: 0.15 ETH on Ethereum, 0.0015 ETH on Base and Robinhood Chain, 0.008 BNB on BSC and 0.025 SOL on Solana.
  • A run pays its own gas from what it spends, at most 10% of it on the EVM chains and 2% on Solana. On Ethereum, where gas is dear, buys wait until the pool is deep enough for that.
  • On Solana the liquidity bucket also repays the rent of the coin’s liquidity position there, at most 0.015 SOL.
Setting the vaults up is part of the launch fee. When the creator and community shares together are above about 62% and the split also buys, those wallets can profit by trading around the vault’s own buys; the form warns about it.
B.6

Tokenomics v2

B.6

Tokenomics v2 is set on the launch form and works on all five chains. The creator’s share of the trading fees goes to the coin’s vault, which spends it as set at launch.

  • On each chain, the creator sets shares for burn, buy and hold, liquidity, holders, and up to 10 payout wallets, adding up to 100%.
  • Holders are paid in the chain’s coin, in one token, or in a basket of up to 5 tokens.
  • The shares and the reward tokens are fixed at launch. Each payout wallet can hand its slot to a new address in two steps. The vault’s owner or the platform can later change who is paid: the minimum holding (never below 0.01% of supply for a coin launched on v4, on its EVM chains and on Solana (0.01%); a coin whose EVM vault was made by an earlier tokenomics factory keeps 0.1%, as its vault's code is fixed; one setting covers a launch's chains, so a new launch starts at its chains' highest floor) and whether every eligible holder, the top holders or holders picked at random are paid.

When it runs

  • Holder payouts run every 5 minutes. A holder is sent rewards once what it is due covers the payout’s cost; until then it stays owed and can claim.
  • There is no fixed minimum: Pangea's runner spends a bucket, or pays the wallets, once what it would move is worth 20 times the run's gas, so the run's gas repayment (at most 10% of what it moves) always covers it. Pangea may also set a minimum per chain, 0 unless set; a vault made before this change keeps the minimum it was made with (0.15 ETH on Ethereum, 0.0015 ETH on Base and Robinhood Chain, 0.008 BNB on BSC; 0.025 SOL on Solana for now). Its buys are at least 15 minutes apart on EVM for a vault made before v4; a v4 coin's EVM vault needs no gap (Pangea's setting, 0 seconds unless set, at most a day); on Solana the gap is Pangea's setting, 3 seconds unless set (3 seconds to a day), and one buy may move the price only a bounded amount.

What a payout costs a holder

A holder sent rewards in the chain’s coin pays for its own payout: at most 10% of what it is due, and at most the figure below for its chain. A holder who claims for itself pays no fee, only its own transaction (on Solana, plus a one-time rent for its payout record).

ChainMost a payout charges a holder
Ethereum0.002 ETH
Base0.0002 ETH
Robinhood Chain0.0002 ETH
BSC0.001 BNB
Solana0.002 SOL
On Solana the program allows at most 0.005 SOL.
B.7

Fee collection

B.7

Fees are paid in the quote asset: ETH, BNB or SOL. Never in the token that was launched.

Meteora supports collecting fees directly in the quote asset. Uniswap v4 collects fees in whichever token enters a swap, so fees from sells accrue in the launched token.

To convert them, the platform swaps those tokens for the quote asset when fees are claimed. This keeps extra code off the trading path, reducing the risk of a bug disrupting swaps. The trade-off is a small price impact from selling the collected tokens into the pool.

Sheet CWhat is built
C.1

Launch types

C.1

Every launch uses the same structure on each of its chains, single-sided liquidity and locked positions. The launch type determines the trading pair and any additional features.

StandardLive

A plain pool paired with each chain's native asset. No hooks means standard swap events stay compatible with DexScreener and other aggregators.

Stock-pairedLive

Each chain's pool pairs with that chain's native coin or with a tokenized stock listed on that chain, chosen per chain in the launch form's "Pairs with" dropdowns. Every pool still opens at the same USD market cap.

HooksWith trade-offs

Uniswap v4 supports hooks, but Meteora has no equivalent, so a hook-based launch cannot use the same setup on all five chains. Custom swap accounting can also break the standard swap events aggregators read.

C.2

PANGEA tokenomics

C.2

$PANGEA is live. It launched through Pangea on all five chains, with the same five-chain structure as every other token. Trade $PANGEA →

ChainOfficial $PANGEA contract
Ethereum0x6434e57e8d12B92bb4D0b1EFb3274748B75196ea
Base0xA551dEF8a44ea423CaA58DA45b3F5764004286Ea
Robinhood Chain0x30de7AE4c9578D110a97367EDe9F7765Cdce06ea
BSC0xd0cb4279e2B74c2d72EEa20Df308Fb9604E3d6Ea
SolanaJAFqCQSpy5YinrsmixWSMaD7jTn4MytJRkyWvYPnY6ea
These five are the only official $PANGEA contracts. Any launch may use the ticker PANGEA; a token at any other address is not this one.
C.3

Team allocation

C.3

The team holds about 19% of the total supply of $PANGEA, which is 5B summed across the five chains. Most of it is locked: about 14.4% sits in vesting contracts and is released in equal monthly steps over two years. The remaining 4.18% is a marketing treasury. Nothing is held in an anonymous wallet: every address below can be checked on chain.

0.0%Team
  • Locked team allocation14.4%
    Released over 24 months718M
  • Marketing treasury4.18%
    For marketing and growth209M
  • Everyone else81.5%
    Pools, holders, the market4073M
Release schedule~0.60% of supply / month
Month 1Month 12Month 24
Where it is locked
EthereumTeam Finance
222.9M
RobinhoodTeam Finance
228.8M
BNB ChainTeam Finance
55.0M
SolanaStreamflow
211.0M

Locked allocation

The locked tokens are not in a team wallet. They sit in Team Finance lock contracts on Ethereum, Robinhood and BSC, and in a Streamflow vesting stream on Solana. Each releases in equal monthly steps over 24 months, about 0.6% of the total supply a month across the four. No one can withdraw ahead of that schedule: the three Team Finance locks have no owner (it was handed to 0x…dEaD on 2026-10-24, which ends their emergency withdrawal and revocation), and the Streamflow stream cannot be cancelled by its sender or its recipient.

ChainLockLocked
Ethereum0xbfae7682f5550d0a9572f750906c4c4e54502ee1222.9M
Robinhood0xBe17cDf528aBb0797B8DD8e3d47E43A2bAF9cb34228.8M
BSC0xc362b1d31bc07712b9eba15755daeaed47a0b0c155.0M
Solanaou8M2oj3byMTRmaxispdbh82pKmew7NLDt6Yy36R6zW211.0M

Marketing treasury

The treasury funds marketing and growth. It is one address on every EVM chain, 0x402908cd6eB5176A1B9FdE3690CCccF41d7D54D0, named pangeatreasury.eth, pangeatreasury.base.eth and pangeatreasury.bnb.

Balances were read on chain when this was written; the explorers (Etherscan, Robinhood's Etherscan, BscScan and Solscan) show them live, along with each lock's release schedule.
Sheet DCrossings
D.1

Bridging

D.1

The token uses LayerZero OFT. Transfers burn tokens on the source chain and mint them on the destination, keeping the total across the five chains unchanged. There are no wrapped tokens.

A token with the price-gap fee is the exception. When a transfer arrives on a chain where the token’s price is higher than on the chain it left, the destination may hold back part of it as the gap fee. The part held back is never minted, so it is burned: the total supply goes down by exactly that amount.

Each chain’s pool receives its tokens at launch (1 billion, or its share of the 1B in degen mode), so all of a launch’s pools open at once.

In degen mode Solana’s supply stays at 1B: when tokens are bridged to Solana, the same number is burned from its bridge reserve, and when tokens leave Solana, the same number is added back to it. Degen mode →

Worked exampleevery chain starts on its 1000M slice
Burn on
Mint on
250M
Ethereum1000M
Base750M
Robinhood1000M
BNB Chain1000M
Solana1250M
Across the five chains5000M

Standard or fast

A standard transfer takes LayerZero’s own path. On every route the verifiers attest once the source transaction has 1 confirmation, and a transfer typically arrives in 4 to 16 seconds. From Ethereum, whose blocks are slower, it takes longer. How to bridge, step by step →

A fast transfer arrives in seconds: a relayer pays the recipient at once from its own tokens and is repaid when the standard transfer lands. It costs the fast lane’s own 0.2% on top of the standard 0.2%, plus the relayer’s price, which is capped at 10%. The bridge screen offers it only when a relayer is quoting your transfer. Relayers →

What a transfer costs

  • The token’s fee. The token keeps 0.2% of every transfer between chains. It goes to the platform. On a token from before factory v3 it is fixed in the token contract on the four EVM chains and nobody can change it; on Solana, the fee split is enforced by Pangea's splitter program. A factory v3 token's fee is its chain's registry figure instead, and a transfer to a dearer chain also pays the price-gap fee (below).
  • The message fee. LayerZero’s price for carrying the transfer, paid in the source chain’s native asset. It is quoted for the exact transfer before you sign.
  • A Solana route’s setup. The first transfer on a Solana route nobody has used yet also pays a one-time setup fee for that route. Every later transfer on it does not.

Factory v3 tokens: the price-gap fee

  • The bridge fee is set per chain. On a factory v3 token the fee is not fixed in the token: each chain's gap registry sets it, from 0 to 0.5% (0.2% unless changed), and the token reads it at every send. Always take what arrives from the token's own quoteOFT (or the quote), never from a fixed 0.2%.
  • The gap burn. When the token is dearer on the destination than where it left, the destination withholds part of what arrives: the price gap above the fees and a small allowance. The part withheld is never minted, so it is burned (the supply figures take it off). A transfer from a dearer chain to a cheaper one pays no gap fee.
  • The least credited (minCredited). Every v3 send names the least the destination may credit after the gap fee. If it would credit less, the arrival waits at the destination and lands once the price allows it. Nothing is burned while it waits, but it cannot be cancelled: it lands only once the gap narrows enough, or when Pangea turns on the token's escape, which may take as long as the gap lasts. The bridge sets it from the estimate less your tolerance (1% by default, at most 20%).
  • Held arrivals. An arrival the destination cannot price yet (its pool unreadable, or no price on record) is held, not charged and not refused, and lands when it can. The quote says so (gap state held).
  • Large transfers and the price log's capacity. Each chain's price log can take only so much in one short window. A large send out of Solana may be refused until more prices are on record (GapNeedRecords: record the price, then send again), and a large arrival into Solana may wait at the destination until the window holds enough records.
  • Out of an EVM chain: the price record first. A v3 send prices on the source token's price log: one entry per second, written by poke() (anyone, from a plain wallet, not a contract; network fee only, with a gas limit of at least 650,000) or by an earlier send. With too few recent entries (fewer than 2 in the last 30 seconds, 384 on Ethereum) or too little capacity left, quoteSend and send revert GapNeedRecords() (0x9a883013). Call the token's poke() in two different seconds, then send within the window. gapMaxSend() returns (maxAmountLD, perEntryLD, windowSec, minBlocks): the most one send carries now and what each new entry adds. A larger send goes in parts.
  • Out of Solana: the price record first. A v3 token's Solana send prices on the store's shared price log. When it holds no recent price, the wallet first signs a small record_price transaction (its signature fee only, no rent), then the send a moment later.
  • A v3 store's first opening. The first opening of a v3 token's Solana routes also creates its price log, about 0.071688 SOL of rent, paid by whoever opens it (in the setup fee shown before signing). Anyone may create it; a launch that bought "Open all bridge routes at launch" was charged for it, and if someone else creates it before the launch opens the routes, that part is not spent and is not refunded.

Routes opened at launch

A launcher can tick “Open all bridge routes at launch” on the launch page’s review step. The launch fee then also covers the setup of all four Solana routes, to Ethereum, BNB Chain, Base and Robinhood Chain, at cost plus the launch fee’s usual margin, in the same one payment. Pangea opens the four right after the Solana launch lands, so nobody pays a setup fee on that token’s routes. The box is offered only while all four routes are configured on Solana. The launch screen shows each route as it opens; a route that cannot be opened automatically is opened by hand.

A transfer of 1,000 tokens: 2 go to the platform and 998 arrive.

Transfers are rounded down to six decimal places. Amounts smaller than 0.000001 token are dropped.
D.2

How to bridge

D.2

Bridging moves your tokens from one chain to another. It is the same token on both sides: the tokens are burned where you send them from and minted where they arrive, so nothing is wrapped and the total across the five chains does not change.

On the Bridge page

  • Pick the token. Open Bridge in the menu and choose the token, or open a token’s page and use its bridge tab. A link with the token in it, such as /app/bridge?token=ptest4, opens it ready.
  • Pick From and To. The chain your tokens are on now, and the chain you want them on. The swap button between them turns the route around.
  • Connect a wallet. An EVM wallet for Ethereum, Base, Robinhood Chain and BSC, a Solana wallet for Solana. A route between an EVM chain and Solana needs both: one sends, the other receives.
  • Enter the amount. Your balance on the From chain is read from the chain. The page shows what you send, the token’s fee, what arrives, and the message fee, before you sign anything.
  • Choose Standard or Fast. Standard is the token’s own path. Fast appears only when a relayer is quoting your transfer.
  • Sign. One transaction on the From chain for a standard transfer. A fast transfer may ask for an approval first. The first transfer on a Solana route that nobody has used yet may ask for a setup step first; see below.
  • Wait for it to arrive. The page follows the transfer and says when it has landed. Recent transfers are listed under the card.

What it costs

  • The token’s fee, 0.2%. Kept by the token for the platform, out of the amount you send. 1,000 tokens sent, 998 arrive. (A token from before factory v3. A factory v3 token's fee is its chain's registry figure, 0.2% unless changed, at most 0.5%.)
  • A factory v3 token: the price-gap fee. A transfer from the chain where the token is cheaper to the chain where it is dearer also pays the price gap, burned at the destination. The quote shows the estimate before you sign; the other way is free.
  • LayerZero’s message fee. Paid in the From chain’s native asset: ETH on Ethereum, Base and Robinhood Chain, BNB on BSC, SOL on Solana. It pays the verifiers and the delivery on the other side, and it is quoted for your exact transfer before you sign.
  • A factory v3 token into Ethereum: Pangea's delivery. LayerZero's executor does not deliver there, so Pangea's delivery keys land the transfer; their cost is part of the network fee, in the From chain's coin (out of Solana, in SOL), and the quote shows it as its own line (Pangea delivery).
  • A factory v3 token into Solana: the Solana delivery. Pangea's delivery key lands the transfer on Solana; its cost is withheld from what arrives, in the token, and shown as its own line (Solana delivery). A new wallet's first arrival also pays its token account's rent from it.
  • A transfer too large for one send: every part pays. Sent in parts, each part is its own transfer and pays the full network fee (LayerZero, and any delivery above); the quote shows the fee per part and the total for all parts before the first signature. A co-signed transfer (one send) shows its fee and its guaranteed minimum before you sign it.
  • Gas. The transaction itself, on the From chain, as for any transaction there.
Route, 1,000 PTEST4Message fee quoted
Base to BSC0.000110 ETH
Ethereum to Base0.000205 ETH
Base to Solana0.000425 ETH
Solana to Base0.00058 SOL

Those are live quotes from 19 October 2026, to show the size of the fee; it moves with gas prices. A transfer into Solana costs more because it also pays for the token account the tokens arrive in.

How long it takes

A standard transfer waits for its transaction to have 1 confirmation on the From chain, then for two verifiers to attest it, and is then delivered. Measured on mainnet, transfers between Base, BSC, Robinhood Chain and Solana took 4 to 16 seconds:

RouteSeconds, sent to deliveredTransfers measured
Base to BSC52
Base to Robinhood Chain4 to 54
Base to Solana5 to 74
BSC to Base81
Robinhood Chain to Base10 to 164
Robinhood Chain to BSC71
Robinhood Chain to Solana5 to 132
Solana to Base12 to 154

From Ethereum it takes longer. Ethereum makes a block only every 12 seconds, and the verifiers wait for the transfer’s block to be confirmed before they attest, so expect about half a minute or more. No transfer out of Ethereum has been measured on mainnet yet, and the Bridge page currently shows the same estimate for every route.

The fast lane

Fast is an option on top of the standard transfer. When you choose it, the transfer carries a public order on Pangea’s fast bridge, PangeaFastBridge, one shared contract per chain. A relayer sees the order and pays you on the To chain within seconds from its own tokens, and is repaid when your standard transfer lands. It costs the fast lane’s 0.2% and the relayer’s price on top of the standard 0.2%, and the price is fixed in the order before you sign. If nobody fills the order, your tokens still arrive the standard way and you get the relayer’s fee too. How relayers work →

  • Fast appears only when a relayer is quoting your route and amount. Otherwise only Standard is offered.
  • Into Solana it needs the token to allow a 232-byte message. Tokens launched from the current factories allow 256; some earlier launches allow 200, and for those the page offers Standard only into Solana.
  • A fast transfer may need an approval first: the fast bridge moves the tokens for you, once.

Solana

  • Into Solana, the recipient is a wallet. Name the Solana wallet that will own the tokens, not a token account. The tokens arrive in that wallet’s token account for the token, which is created on delivery if it does not exist.
  • That account’s rent is in the message fee. Every transfer into Solana carries 2,074,080 lamports (about 0.002 SOL), the rent of a new Token-2022 account, paid in the From chain’s asset as part of LayerZero’s fee. You need no SOL to receive. For a factory v3 coin today, the first arrival to a new wallet instead has that rent withheld from the tokens that arrive (the quote’s gap.delivery.ata), and an arrival too small to pay it waits on Solana until the account exists (gap.warning).
  • The first transfer on a route pays its setup. A token’s route between Solana and a chain is opened the first time anyone uses it. That first transfer also pays a one-time setup fee: the rent of the accounts LayerZero keeps for the route. The page shows it as a separate line and may ask for two steps. Every later transfer on that route, by anyone, does not pay it.
  • Out of Solana, you pay in SOL. The message fee and the transaction fee are paid from your Solana wallet, so keep a little SOL there.
  • Amounts cross at six decimals. The odd base units below 0.000001 token stay in your wallet.

If a transfer seems stuck

  • Look it up on LayerZero Scan. Paste the transaction hash or signature of your send at layerzeroscan.com. It shows each verifier’s attestation and the delivery, with links to both transactions.
  • Inflight or confirming. The verifiers are waiting for the confirmation or attesting. Wait: this is the ordinary state for the first seconds, and longer from Ethereum.
  • Verified but not delivered. Delivery is open to anyone once a message is verified. LayerZero’s executor delivers it, and Pangea runs its own delivery service that delivers every Pangea token’s messages as soon as they are verified. If one sits verified and undelivered for more than a few minutes, anyone can deliver it by calling the destination endpoint’s lzReceive with the message, at their own gas cost. The exception: a factory v3 token's charged arrival into Solana, which only Pangea's appointed deliverers may land.
  • Into Solana, on a route that is not open. A transfer sent into Solana before its route is opened waits, and is delivered once the route is opened; LayerZero Scan may show it as blocked until then. Nothing is lost in between. The Bridge page opens the route as part of the first transfer on it.
  • A held fast order. When a fast order cannot be paid on the To chain, the fast bridge holds it for the sender. The Bridge page shows a button that sends it back; anyone may do it, paying LayerZero’s fee for the trip back.
  • Nothing on LayerZero Scan. The send did not go through on the From chain. Check it on that chain’s explorer: if it failed, the tokens never left your wallet.
D.3

LayerZero routes

D.3

One token, five contracts

  • An address per chain. A token is a LayerZero OFT contract on each of Ethereum, Base, Robinhood Chain and BSC, and a mint on Solana, held by LayerZero’s OFT program. The five addresses are all different (or, with “Same contract address on all EVM chains”, one on the four EVM chains and the mint on Solana) and all end in 6ea. They are on the token’s page and in /api/pools.
  • 1B per chain. Each chain mints its own 1 billion at launch, all of it into that chain’s pool, 5 billion across the five. On an EVM chain launchSlice() is that 1B and launchTotal() the 5B (on a launch on fewer chains, 1B for each of them). Nothing can mint more: there is no mint function and no minter. In degen mode each chain mints its equal share of 1B instead, and launchTotal() is the 1B.
  • Burn and mint. A transfer burns the tokens on the chain it leaves and LayerZero’s message mints the same number on the chain it reaches. On an EVM chain the token’s fee is moved to bridgeFeeTo() and only the rest is burned, so what is burned on one side is exactly what is minted on the other. A factory v3 token's price-gap fee is the exception: the destination withholds it and never mints it.
  • What is on a chain, and what is reported. On each EVM chain localSupply() is what is on that chain now; it moves as tokens cross. totalSupply() is a reported figure: it starts at the chain’s 1B, and for tokens with a SupplyReporter the platform’s reporter on that chain mirrors the token’s supply on Solana into it. A report moves no balance and mints nothing, and a factory v1 token refuses one below its own localSupply() or above launchTotal(). Earlier launches have no reporter and always report their slice. A degen mode token reports the 1B total and takes no reports.
  • Peers are sealed at launch. A peer is the token’s counterpart on another chain, the only address it accepts a mint from. The factory registers the four peers in the transaction that creates the token and hands ownership to a LaunchOwner contract that is sealed from the start: sealed_() is true and it has no way to add, change or remove a peer.
ChainSupplyReporter
Ethereum0xD7AF0c412911DCE5F864d949abA14a3048DD87B2
Base0xF10254C126e41F69799e781D185cc42c99EE37Ed
Robinhood Chain0x51D658a71AEa38f123d4c73e42703Fb9C949900A
BSC0xf1a98Fa0B2baB64BC4358dC0C2c13444ecCA365b

How every route is set up

A route is one direction between two chains, for one token. When the factory creates a token it is, for that one transaction, the token’s LayerZero delegate, and it writes every route from a table fixed in its own constructor. Nothing is left on LayerZero’s defaults, because a default can be changed by whoever administers the endpoint.

  • Libraries. The send and the receive message library are named explicitly: LayerZero’s Ultra Light Node on every chain, the same contracts the endpoint’s defaults point at today, pinned so they stay.
  • Executor. LayerZero’s executor on each chain is named explicitly, with a cap on the message size.
  • Verifiers. No required verifiers. An optional set of Pangea’s own verifier plus the external ones, and 2 of the set must attest before a message counts. So no single party can deliver a message: not Pangea’s verifier, and not any one external one.
  • Confirmations. 1 on every source chain, Solana included: the verifiers attest once the send has 1 confirmation. Both ends of a route hold the same number.
  • A gas floor. Every transfer must give the delivery at least 100,000 gas on an EVM chain, and 200,000 compute units plus 2,074,080 lamports on Solana, for the recipient’s token account. It is an enforced option, added to every send, so a sender cannot under-pay delivery.
  • A message size. Up to 10,000 bytes between EVM chains and from Solana. Toward Solana 256 bytes for launches from the current factories; the factory admin can move that between 200 and 800 for launches after, never for a token that exists. A Solana transaction is small, and a cap above what Solana can deliver would let a message be sent that can never land.
ChainOptional verifiersOf which must attestConfirmations
EthereumPangea, LayerZero Labs, Nethermind, Horizen, Canary21
BasePangea, LayerZero Labs, Nethermind, Horizen, Canary21
Robinhood ChainPangea, LayerZero Labs, Nethermind21
BSCPangea, LayerZero Labs, Nethermind, Horizen, Canary21
SolanaPangea, LayerZero Labs, Nethermind, Horizen, Canary21

That is the set on each chain, for its routes to Ethereum, Base, BSC and Solana. Robinhood Chain has two of the external verifiers, so a route with Robinhood Chain at either end uses the verifiers both chains have: Pangea, LayerZero Labs and Nethermind, 2 of 3. Every other route has 5, 2 of 5.

ChainSend libraryReceive libraryExecutor
Ethereum0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe10xc02Ab410f0734EFa3F14628780e6e695156024C20x173272739Bd7Aa6e4e214714048a9fE699453059
Base0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda20xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4
Robinhood Chain0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de70xe1844c5D63a9543023008D332Bd3d2e6f1FE10430x4208D6E27538189bB48E603D6123A94b8Abe0A0b
BSC0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC10x3ebD570ed38B1b3b4BC886999fcF507e9D584859
Solana7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVHAwrbHeCyniXaQhiJZkLhgWdUCteeWSGaSN1sTfLiY7xK
ChainLayerZero endpointLaunch factory (v2)Earlier launches (v1)Pangea’s verifier
Ethereum0x1a44076050125825900e736c501f859c50fE728c0xf7052a4E38e241bc66bfAe6eB27D10B2A42cacB40x1D583e6eA4AA7c5e167a7E69B07b83DD5B813f530x5FA03555f186e53a644405817E1BeAC894Bd6562
Base0x1a44076050125825900e736c501f859c50fE728c0xb405b396b607C3482A3d301b1cFF57A7de75325a0xba4beE425F268f7Dc0F7e5457d14320d5A64cd060x6dbFda44baB6bf0DDF3E9F88d7823eDdB48f4b25
Robinhood Chain0x6F475642a6e85809B1c36Fa62763669b1b48DD5B0x9Cf5832474CBB09D2E06FF20460aff4303fAf72A0x8A1F3e1DB07048c007F6d9ab924494C77c0DBa650x85880e7D5315365165E97620Ba17B48A645b13F3
BSC0x1a44076050125825900e736c501f859c50fE728c0x92858095Cadf9f9d60dC17226f7FF15Be4b69e450xd1454764E8D87B117a23491E77cAfcCC504302560xF61Ed4347c4D02522e96657a606f49E8381B3dCA
SolanaULN 7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVHSplitter program Hb5uTppRbNxc3RjVRqTWqfdU3E3GiZyGYKccxYf9tUejThe same program7EgbAY7663cybirqgmFHzF8q9debeePxr8ra7wKyEmNE
VerifierEthereumBaseRobinhood ChainBSCSolana
LayerZero Labs0x589dEDbD617e0CBcB916A9223F4d1300c294236b0x9e059a54699a285714207b43B055483E78FAac250xd01ae6905d48315f7bE10C7330aeCF8360Ef5b120xfD6865c841c2d64565562fCc7e05e619A30615f04VDjp6XQaxoZf5RGwiPU9NR1EXSZn2TP4ATMmiSzLfhb
Nethermind0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA50xcd37CA043f8479064e10635020c65FfC005d36f60x0Ffe02DF012299A370D5dd69298A5826EAcaFdF80x31F748a368a893Bdb5aBB67ec95F232507601A73GPjyWr8vCotGuFubDpTxDxy9Vj1ZeEN4F2dwRmFiaGab
Horizen0x380275805876Ff19055EA900CDb2B46a94ecF20D0xa7b5189bcA84Cd304D8553977c7C614329750d99not on this chain0x247624e2143504730aeC22912ed41F092498bEf2HR9NQKK1ynW9NzgdM37dU5CBtqRHTukmbMKS7qkwSkHX
Canary0xa4fE5A5B9A846458a70Cd0748228aED3bF65c2cd0x554833698Ae0FB22ECC90B01222903fD62CA4B47not on this chain0xfA9bA83C102283958B997Adc8B44ED3A3CdB5dDa7jMeX5mzXnSSKYd8DxBDP4xMnkNFZZZm5W28FWUTbwU3

On Solana the same is done by Pangea’s splitter program. The program holds one route setting per destination chain, and the first transfer on a token’s route applies it to that token, once: the libraries, the executor, the verifiers in both directions, the confirmations and the gas floor. The token’s LayerZero admin and delegate on Solana is an address only that program can sign for, and the program has no instruction that changes a peer, a library or a verifier afterwards.

Why nobody can change a route later

  • On an EVM chain the factory writes the routes while it is the token’s delegate, then never calls the endpoint for that token again and has no function that could. The token’s owner, the sealed LaunchOwner, has no way to name another delegate. So no key, including Pangea’s, can change a token’s libraries, executor or verifiers.
  • This matters because a delegate who could name the verifiers could name itself, attest a message nobody sent, and mint. Sealing the peers alone would not close that.
  • One thing stays adjustable on purpose: the gas floor. The LaunchOwner’s admin can raise or lower it, so that a chain changing its gas costs does not break bridging for good. It accepts exactly one lzReceive option and nothing else, under hard caps of 1,000,000 gas and 10,000,000 in value, so it cannot add a payment to anyone or set a floor nobody can pay. On Solana the gas floor is fixed when the route is opened.
  • The other side of this: if LayerZero retires a verifier a route depends on, that route keeps working only while two of its verifiers still attest, and nobody can swap one in. A paused bridge can be lived with; a token anyone could mint could not.
  • On Solana the admin can change the route setting for launches still to come. A token’s route is applied with the setting in force when it launched, and the program refuses a setting edited after that, unless the admin signs the opening; once a route is open nothing changes it.

Check it yourself

Everything above can be read from the chains, with no help from Pangea. The worked example is PTEST4, a live test launch, on Base. Its address there is 0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea; the other addresses are in the table at the end. These are read-only calls, with Foundry’s cast and any RPC endpoint for the chain.

The token's peers, owner and delegate, on Base
TOKEN=0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
ENDPOINT=0x1a44076050125825900e736c501f859c50fE728c
RPC=your-base-rpc-endpoint

# its counterpart on Ethereum, BSC, Robinhood Chain and Solana
for EID in 30101 30102 30416 30168; do
  cast call $TOKEN "peers(uint32)(bytes32)" $EID --rpc-url $RPC
done

# the owner is a LaunchOwner, sealed: true
OWNER=$(cast call $TOKEN "owner()(address)" --rpc-url $RPC)
cast call $OWNER "sealed_()(bool)" --rpc-url $RPC

# the delegate, the only key that can set verifiers, is the factory that launched it
cast call $ENDPOINT "delegates(address)(address)" $TOKEN --rpc-url $RPC
One route's libraries, executor, verifiers and gas floor: Base to Robinhood Chain
EID=30416
SEND_LIB=$(cast call $ENDPOINT "getSendLibrary(address,uint32)(address)" $TOKEN $EID --rpc-url $RPC)
cast call $ENDPOINT "isDefaultSendLibrary(address,uint32)(bool)" $TOKEN $EID --rpc-url $RPC
cast call $ENDPOINT "getReceiveLibrary(address,uint32)(address,bool)" $TOKEN $EID --rpc-url $RPC

# verifiers: (confirmations, required count, optional count, threshold, required, optional)
ULN=$(cast call $ENDPOINT "getConfig(address,address,uint32,uint32)(bytes)" $TOKEN $SEND_LIB $EID 2 --rpc-url $RPC)
cast decode-abi "f()((uint64,uint8,uint8,uint8,address[],address[]))" $ULN

# executor: (max message size, executor)
EXE=$(cast call $ENDPOINT "getConfig(address,address,uint32,uint32)(bytes)" $TOKEN $SEND_LIB $EID 1 --rpc-url $RPC)
cast decode-abi "f()((uint32,address))" $EXE

# the gas floor every send carries
cast call $TOKEN "enforcedOptions(uint32,uint16)(bytes)" $EID 1 --rpc-url $RPC

What those return for this token, read on 19 October 2026:

ReadAnswerWhat it means
peers(30416)0x00000000000000000000000068906d8b2a22d329b853ec650fa6a29fab6056eaThe token on Robinhood Chain, left-padded to 32 bytes.
peers(30168)0xbad1e830d4a3bba670d642dd253151f5564074eb91e9197e0216ec274e91df6eIts OFT store on Solana, DaGUXY1qixx3Wsp7KFb7L6UtXV6QHCRXtaXtx4yfLued, as 32 bytes.
sealed_()trueNo peer can ever be added, changed or removed.
delegates(token)0x025f893Bdcd7978232C211e3A28610FC6d8B8970The factory that launched it, an earlier one than today’s, with no code to touch the endpoint again.
isDefaultSendLibraryfalseThe library is pinned, not inherited.
getConfig(... 2)(1, 0, 3, 2, [], [3 verifiers])1 confirmation, no required verifiers, 3 optional, 2 must attest.
getConfig(... 1)(10000, 0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4)Messages up to 10,000 bytes, LayerZero’s executor.
enforcedOptions(30416, 1)0x000301001101000000000000000000000000000186a0One lzReceive option: 0x186a0, which is 100,000 gas.

The three optional verifiers it returns are Pangea’s on Base, 0x6dbFda44baB6bf0DDF3E9F88d7823eDdB48f4b25, and LayerZero Labs’ and Nethermind’s there. Toward Solana the same read returns 5 verifiers and an executor cap of 200 bytes: this token was launched before the factories raised it to 256. The gas floor toward Solana reads 0x00030100210100000000000000000000000000030d40000000000000000000000000001fa5e0: 0x30d40 is 200,000 compute units and 0x1fa5e0 is 2,074,080 lamports.

On LayerZero Scan, look up the token’s address, or any transfer. Its transfer from Base to Robinhood Chain on 16 October 2026, 0x5860a6920fd6fe6d97f7d88104d6ce105bb43fd4a9654d7d84cdd9229e961f65, shows all three of the route’s verifiers attesting, and the delivery five seconds after the send. Each message lists the verifiers that attested and the lzReceive gas it carried.

On Solana, a token’s route to a chain is open when LayerZero’s receive config account for it exists: the ULN program’s address for the seeds "ReceiveConfig", the chain’s endpoint id as four big-endian bytes, and the OFT store. Its contents are the confirmations and the verifiers, in the same order as above. For PTEST4, read on 19 October 2026, the routes from Base and Robinhood Chain are open, each with 1 confirmation and 2 of its verifiers, and the routes from Ethereum and BSC are not open yet: nobody has bridged on them. Bridging from code → has a script that checks it.

ChainPTEST4 on that chain
Ethereum0x835e9CFB333B9E4811CA1585460442806a99d6eA
Base0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
Robinhood Chain0x68906D8b2A22D329b853ec650Fa6A29fAb6056Ea
BSC0x90Ba4Df685A2D33c12C40F03808F290519E7F6EA
SolanaMint DdJ6R9TSKdNYMZhdgATANT98BJNgScuU58baQoCfZ6ea, OFT store DaGUXY1qixx3Wsp7KFb7L6UtXV6QHCRXtaXtx4yfLued
Pangea’s own checks do the same against every factory before a launch: each route’s two ends must agree, and every verifier a factory names must be on a list kept by hand, with the operator’s name. The addresses on this page are those.
D.4

Relayers

D.4

A relayer pays the recipient on the destination chain within seconds, out of its own stock of the token, and is paid back, plus its fee, when the bridged tokens arrive over the standard path a few seconds later. Nobody has to trust it: the recipient, the amount and the fee are fixed by the order, and a relayer that pays anything else is simply never repaid. If no relayer fills an order, the transfer arrives the standard way, and the fee no relayer earned goes to the recipient.

There is one fast bridge per chain, shared by every token launched on Pangea. A new token can use it the moment its launch lands: nothing is deployed for it. An order names its launch, never a token address, and the bridge reads the token itself from Pangea’s own factories, so a relayer is only ever repaid in, and a recipient only ever paid in, that launch’s own token on that chain.

The fast bridges on Ethereum, Base, Robinhood Chain and BSC are deployed, at the addresses listed at the end of this section. Orders out of Solana are posted by Pangea’s fast-bridge program, in the same format. Into Solana the fast lane needs a token that allows a 232-byte message, which every launch from the current factories does.

What a transfer costs, and who is paid

  • The token’s fee. The token keeps 0.2% of every transfer between chains, on either lane. It goes to the platform.
  • The fast lane’s fee. On the fast lane the fast bridge keeps 0.2% first, also for the platform, and the token’s fee is then taken from what is left. The bridge’s admin can change it, per launch or for all, but never above 1%.
  • The relayer’s fee. This is a price, not a platform fee. Relayers publish what they charge and the app takes the lowest. The bridge refuses any order that would pay a relayer more than 10% of what arrives, or the lower cap its admin sets.
  • Your limit. You set a slippage, 0.5% unless you change it and 10% at most. If the best relayer price is above it, no fast transfer is offered, and the bridge refuses an order that would pay the recipient less.

One transfer of 1,000 tokens, three ways, with a relayer charging 0.1% as the example price:

Who getsStandardFast, filledFast, unfilled
Platform23.9963.996
Relayer00.9960040
Recipient998995.007996996.004
Arrives in4 to 16 secondsseconds4 to 16 seconds

On the fast lane the fast bridge keeps 2 tokens and the token keeps 1.996, so 996.004 are on their way. The relayer pays the recipient 995.007996 at once and receives the 996.004 when they arrive, keeping 0.996004. Both platform fees are taken on the chain the transfer leaves, when it is sent, so a fast transfer that nobody fills has still paid them.

Becoming a relayer

Anyone can be a relayer. There is nothing to apply for and nothing to stake: you publish a price, watch for orders and fill the ones you want. Filling is never an obligation, and leaving an order unfilled takes nothing from anyone. It does count against you if the order was posted at your published price, as described under benching below.

What follows is the whole protocol, two HTTP calls and a handful of contract calls, and any program that follows it is a relayer. The HTTP calls are for the price board only: watching and filling work entirely on chain.

1. What you need

  • The token, on every chain you deliver to. Fills are paid from your own wallet. The fast bridge holds no stock of its own.
  • An approval. On each of those chains, approve that chain’s fast bridge to move your tokens: approve(fastBridge, amount) on the token. Without it your prices are not taken and a fill reverts.
  • Gas. A fill is a transaction on the destination chain, paid in its native asset: ETH on Ethereum, Base and Robinhood Chain, BNB on BSC.
  • An RPC endpoint for each chain. You read OrderPosted logs on the chains orders leave and send fills on the chains they go to, so the endpoint must serve eth_getLogs. A WebSocket endpoint makes you faster: subscribed to the fast bridge’s logs it tells you about an order the moment it is posted.
  • One key. The address that signs your prices is the address the app checks for tokens and approval, so sign with the wallet you fill from.

Check a bridge before you approve it. tokenOf(launchId) returns the token it moves for a launch, read from Pangea’s factories (isFactory(factory) says which it accepts), and remoteBridge(eid) its counterpart on another chain.

2. Publish your price

You send a signed price sheet to the price board. The board is public: read it to see the prices you are competing with, the ceiling, and who is benched. A route is a pair of chains on one shared bridge, so one price covers every token on that route; the app only takes it for a transfer when you hold that token on the destination.

Publish, POSThttps://www.pangea.trade/api/bridge/relayer-quotes
Read, GEThttps://www.pangea.trade/api/bridge/relayer-quotes
The body you POST
{
  "sheet": {
    "v": 1,
    "relayer": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "issuedAt": 1760000000,
    "expiresAt": 1760000120,
    "routes": [
      {
        "fromEid": 30184,
        "toEid": 30102,
        "bridge": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
        "feeBps": 10,
        "minFee": "1000000",
        "maxAmount": "50000000000000"
      },
      {
        "fromEid": 30101,
        "toEid": 30102,
        "bridge": "0xcccccccccccccccccccccccccccccccccccccccc",
        "feeBps": 10,
        "minFee": "1000000",
        "maxAmount": "50000000000000"
      }
    ]
  },
  "signature": "0x<65-byte signature, 130 hex characters>"
}
FieldWhat it is
vAlways 1.
relayerYour address. The signature must be this address’s.
issuedAt, expiresAtUnix seconds. A sheet may live at most 300 seconds, and its issue time may be at most 120 seconds ahead of the server’s clock.
routesOne to 64 routes. The same route may not appear twice.
fromEid, toEidThe LayerZero endpoint ids of the chain an order leaves and the chain you deliver on.
bridgeThe fast bridge on the chain the order leaves. Not the one you fill on.
feeBpsYour price in basis points of what arrives, a whole number from 0 to the board’s ceiling. The ceiling is 1000, which is 10%, unless the board reports another.
minFeeThe least you charge whatever the size, in base units, as a decimal string. Gas does not shrink with the amount.
maxAmountThe most you will front on that route, in base units, as a decimal string above zero.
solanaOptional, and needed for any route that delivers to Solana: your Solana address. The body then also carries solanaSignature, your Solana key’s Ed25519 signature over the same text, in base58 or base64. On a route from Solana, bridge is 32 bytes, written as 0x and 64 hex digits.
launchesOptional: up to 512 lines of toEid, launchId, minFee and maxAmount, to price each launch on its own -- every launch shares one fast bridge, and a route's amounts are base units of whichever token is sent. A destination with any launch line is quoted only for the launches it lists, at the route's feeBps and the line's minimum and limit (never above the route's own). Each is signed as launch <toEid> <launchId> min <minFee> max <maxAmount>, after the routes.

The signature is over the text below, signed as an Ethereum personal message: EIP-191, which wallets offer as personal_sign. The lines are joined by a single newline with none at the end, addresses are in lower case, and the routes are sorted by fromEid, then toEid, then bridge, whatever order the JSON lists them in. This is the text for the body above:

The text you sign
Pangea relayer quotes v1
relayer: 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
issued: 1760000000
expires: 1760000120
30101>30102 0xcccccccccccccccccccccccccccccccccccccccc 10bps min 1000000 max 50000000000000
30184>30102 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 10bps min 1000000 max 50000000000000

Your fee for a transfer is feeBps of what arrives, rounded up, and never less than minFee. If that comes to more than the ceiling allows on that transfer, or the transfer is larger than maxAmount, you have no price for it.

A sheet stops counting at its expiry, so publish again before then. Every 30 seconds is plenty, and the board accepts 120 publishes per caller in ten minutes. Each new sheet replaces your last one and must carry a later issuedAt. Stop publishing and you are off the board when your last sheet expires.

AnswerWhat it means
200Kept. The body is { ok, relayer, expiresAt }.
400The sheet is malformed, expired or over the ceiling. The reasons are in error.
401The signature is not this relayer’s over this sheet.
403This address may not publish on this deployment. The board is open to any address unless the deployment lists who may publish or has turned an address away.
409A sheet issued at the same time or later is already held for this relayer.
413The body is larger than 131,072 bytes.
429Too many requests. The message says how many seconds to wait.
424The board cannot be read or written right now.
What a GET returns
{
  "now": 1760000030,
  "maxFeeBps": 1000,
  "sheets": [
    {
      "sheet": "<a sheet, as published>",
      "signature": "0x<its signature>"
    }
  ],
  "benched": [
    {
      "relayer": "0x<address>",
      "until": 1760003600
    }
  ],
  "neverBenched": [
    {
      "relayer": "0x<address>",
      "strikes": 1
    }
  ]
}

Reading is limited to 300 requests per caller in ten minutes. maxFeeBps is the ceiling in force, sheets every live sheet with its signature, and benched the relayers whose prices are not being taken, with the unix second each bench ends. neverBenched lists the relayers this deployment never benches that have strikes within the hour, with how many.

How the app chooses. For each transfer it works out every live sheet’s price and looks at the 4 lowest, lowest first. Between equal prices the sheet with the earlier issuedAt comes first. It takes the first whose relayer holds what the fill pays, in that launch’s token on the destination chain, and has approved the fast bridge there for it. Benched relayers are left out. If that price would leave the recipient less than the sender’s slippage allows, no fast transfer is offered. When no relayer quotes a transfer at all, the fast lane is simply not shown.

3. Watch for orders

Every fast transfer is an order, posted on the fast bridge of the chain it leaves. That bridge emits one event for it:

On the source chain’s fast bridge
event OrderPosted(
    bytes32 indexed orderId,
    bytes32 indexed launchId,
    uint32 dstEid,
    bytes32 indexed sender,
    uint64 senderNonce,
    bytes32 to,
    uint256 amount,
    uint256 relayerFee,
    uint64 lzNonce
);
topic00x7e20a23f868a6866f04de222fcf771b4747be0d748f6c3af50083f019a56c231
FieldWhat it is
orderIdkeccak256(abi.encode(uint32 srcEid, bytes32 srcBridge, bytes32 launchId, bytes32 sender, uint64 senderNonce, uint32 dstEid, bytes32 to, uint256 amount, uint256 relayerFee)).
launchIdThe launch the order is for. The token on either chain is tokenOf(launchId) on that chain’s bridge.
dstEidThe endpoint id of the chain to deliver on.
sender, senderNonceWho posted the order, as 32 bytes, and that sender’s own counter on that bridge.
toThe recipient, as 32 bytes: an EVM address left-padded with zeros, or a Solana owner’s public key. An EVM destination refuses a recipient whose top 12 bytes are not zero.
amountWhat arrives on the destination, in base units. Not what the sender parted with: both platform fees are already out of it.
relayerFeeWhat the filler keeps, out of amount, in base units.
lzNonceThe LayerZero nonce of the message carrying the order. A fill has to name it.

Orders from Solana

An order from Solana is posted by Pangea’s fast-bridge program (post_order) and carried by the sender’s own OFT send, in the same Solana transaction. On an EVM destination, for source endpoint 30168, the order’s srcBridge is the sender’s own key (the OFT’s composeFrom), and the sender in the order is always taken to be that key: nobody can post in another’s name.

Verify before you fill. Skipping post_order only skips the fast lane’s fee, so an EVM-side check cannot tell you the order was paid for. Before filling an order from Solana, read the Solana transaction yourself and fill only when it holds both the post_order instruction and the OFT send it checks, with the same launch, recipient, amount and fee, and the transaction is finalized. A fill on anything less is a bet nobody else is making for you.

4. Fill

A fill is one call, made on the chain the order is going to:

On the destination chain’s fast bridge
function fillOrder(
    uint32 srcEid,
    bytes32 srcBridge,
    bytes32 launchId,
    bytes32 sender,
    uint64 senderNonce_,
    address to,
    uint256 amount,
    uint256 relayerFee,
    uint64 lzNonce
) external returns (bytes32 id);

srcEid is the endpoint id of the chain you saw the event on, and srcBridge the bridge that emitted it, as 32 bytes (the address left-padded with zeros): the destination accepts only a bridge it has named for that chain, else NotARemoteBridge. From Solana, srcBridge is the sender’s own key instead (see below). The other arguments are the event’s own fields, with to as the 20-byte address it pads: senderNonce_ is its senderNonce. In the same transaction the bridge moves amount - relayerFee of tokenOf(launchId) from your wallet to the recipient and records you as owed amount.

Every one of those fields is hashed into the order’s id. Change the source bridge, the launch, the amount, the recipient or the fee and you have filled an order nobody posted: you have paid someone, and nothing will ever repay you.

Check before you send, on the destination bridge. Each check is a read and saves the gas of a fill that would revert:

  • filledBy(orderId) is the zero address: nobody has filled it yet.
  • notYetArrived(token, srcEid, lzNonce), with token the launch’s token on this chain, is true: the order’s own message has not reached this chain. Once it has, there is nothing left to deliver early and a fill is refused.
  • You hold amount - relayerFee of the token there and your approval covers it.
  • relayerFee is at least your own price for that amount. You are never obliged to fill.
RevertWhat it means
NoRemote(srcEid)This bridge has no counterpart on that source chain.
NotARemoteBridge()The srcBridge you named is not a bridge this chain has ever named for that source chain.
UnknownLaunch(launchId)None of Pangea’s factories this bridge accepts knows that launch.
AlreadyFilled()Somebody filled it first. One fill per order.
AlreadySettled()The order has already been paid out.
FeeExceedsAmount()The fee is not less than the amount. No real order looks like this.
TooLateToFill()The message you named has already reached this chain.
A token transfer errorYour balance or your approval is too small.

A fill that goes through emits OrderFilled(orderId, relayer, to, paid).

5. Get repaid

You are repaid on the chain you filled on, in the token, when the order’s own message arrives and the bridge settles it: seconds after the order was posted. The bridge checks that the message came from the launch’s own token and the source chain’s own fast bridge before it pays anyone. settlement(orderId) on the destination bridge says how the order settled:

ValueWhat happened
0Not settled yet.
1Nobody filled it. The recipient was paid everything that arrived, the relayer’s fee included. Event: SettledSlow.
2A fill earned the fee. The filler was sent the whole amount. Event: RelayerPaid.
3A fill did not earn the fee. The filler was sent back amount - relayerFee, what it paid out, and the fee went to the recipient. Events: RelayerPaid and FeeReturned.
4Held. The recipient could not be paid on this chain (it is not an address here), so nobody could fill it and the bridge keeps it for the sender. Event: Refundable(orderId, guid, srcEid, sender, amount). It is never a strike against a relayer.

The same order can only arrive twice from Solana, when a sender reuses a nonce. The second arrival is paid to the recipient (ArrivedAgain and SettledSlow) and the first settlement stands: each arrival brought its own tokens, so none is refused.

Sending a held order back

Anyone may send a held amount back to its sender on the chain it came from: quoteRefund(guid) gives LayerZero’s fee for the message back, and refund(guid), paying that fee, sends it (Refunded). refunds(guid) shows what is held. The bridge screen offers this button itself when a transfer it sent is held.

What earns the fee. The fee pays for delivering early with your own tokens, so a fill earns it only when both of these are true:

  • The lzNonce your fill named is the nonce of the message that settles the order.
  • The settlement lands in a later block than your fill. A fill settled in its own block is repaid what it paid out and earns nothing.

filledBy(orderId) and filledLzNonce(orderId) return the filler and the nonce it named. So fill as soon as you are willing to.

Ceilings

LimitWhat it bounds
MAX_FAST_FEE_BPSThe fast lane’s own fee can never be set above 100 basis points, 1%. Read the fees in force for a launch with feesOf(launchId).
maxRelayerFeeBpsNo order may pay a relayer more than this share of what arrives: 1000 basis points, 10%, unless the bridge’s admin sets a lower one, for all launches or one. An order above it is refused with RelayerFeeTooHigh.
maxFeeBpsThe price board’s own ceiling on a published price, reported by every read of the board.
minReceivedThe sender’s floor, written into the order. An order that would pay the recipient less is refused with ReceivesTooLittle before any token moves.

What gets you benched

When the app sends an order at your published price, it remembers that the price was yours. Each order is judged once: reporting the same transaction again records nothing. It looks at the order again from 2 minutes after it was posted and judges it once it has settled, or after 30 minutes if it still has not. An order nobody filled, or one you filled without earning the fee, is a strike against you.

After 3 strikes within 60 minutes your prices are not taken for 60 minutes, however low they are. Those are the defaults, and a deployment can set its own. Your sheets stay on the board, and benched in the board’s answer says until when.

A deployment can name relayers it never benches, in BRIDGE_RELAYERS_NEVER_BENCH: EVM addresses, or Solana accounts a sheet names and has signed with. These are its own relayers, so nobody can switch its fast lane off by posting orders at its price while its stock is busy. Their unfilled orders are still struck and shown under neverBenched, but the strikes never add up to a bench. When the setting is empty, every relayer can be benched.

  • It is not a strike if any relayer’s fill earned the fee: the sender was served.
  • It is not a strike if another address filled the order without earning the fee within 90 seconds of it being posted and before the block that settled it, because that fill paid the recipient early and shut you out. A fill made later, or in the settling block itself, delivered nothing early, so it does not excuse you.
  • It is not a strike if you could not have filled it. When the app gives your price it reads, at one block of the destination chain, your balance of that launch’s token and your approval of the fast bridge, and it only gives your price if they cover the fill. What you could pay out is the lower of the two. An order is matched to the last quote given before it was posted, if that was no more than a few minutes earlier (an order posted long after its quote counts against nobody), so asking for the quote again later changes nothing. Only when a strike is about to be given, the app subtracts what you have paid out since in other fills of that same launch that served somebody, until the order settled (or until it is judged, if it never did), or 90 seconds after it was posted if that is later. If what is left is less than the order pays, there is no strike. So somebody draining your stock with many small orders first does not turn the larger ones after them into strikes. A fill that would excuse you but has not settled yet is waited for, up to 30 minutes after the order was posted.
  • A fill counts only if it was your own direct call of the bridge’s fillOrder for that launch (not through a contract or a batch, which cannot be read), came from an EVM chain, paid someone other than you, and earned the fee: it named the order’s own message and came before the block that settled it. A fill of an order nobody posted never settles, and one that names the wrong message or lands in the settling block is repaid at once, so neither excuses anything. Fills of orders from Solana do not count, because a plain token send from Solana can make one without paying the fast lane’s fee. Moving your stock away or revoking the approval excuses nothing either, nor does running out of native gas: both are yours to top up. Repayments that arrive in the meantime are not added back.
  • Into Solana only the balance read when the price was given is used, because there is no cheap list of your fills there. Orders into Solana are not judged yet.
  • If a read fails, there is no strike then. The order is checked again later, a few times, and if it still cannot be checked it is dropped without a strike, and the server logs it. A rate limit or a slow node is such a failure. Having more fills in the window than are read, or more in one block than the chain will list, is not: it is no excuse, and neither is a drain made of more than that many fills. The app makes at most a few of these checks a minute, and fewer for any one relayer, so a strike can come a little later than the order was due.
  • A fill does not clear earlier strikes. Each one stops counting 60 minutes after it was given.

The risks you take

Relaying is a business you run with your own money. Nothing here promises that it pays.

  • A reorg on the source chain. Between your fill and your repayment, the fill is unsecured. If the source chain reorganises and the order’s transaction is gone, the message never arrives and nothing repays you. How many blocks you wait before filling is your own trade between speed and that risk. If a reorg keeps the order but moves its message to a different LayerZero nonce, you are repaid what you paid out and earn nothing.
  • Filling before the order is in a block. Acting on pending transactions is the fastest way to fill and the riskiest: a transaction that has not been mined is likelier to vanish than a confirmed one.
  • Inventory. You hold the token on every chain you serve, and its price moves while you hold it. Your fee is paid in the token too.
  • Gas. A fill that reverts still costs gas, and gas is paid in the chain’s native asset while your fee is paid in the token. A price that does not cover gas loses money slowly.
  • Time. Your tokens are out until the order settles. Settlement is run by LayerZero’s executor; if it stalls, anyone may run it, and until someone does you wait.
  • Being shut out. Only one fill per order is accepted. Another address can fill ahead of you, even with a nonce that earns it nothing, and that order is then closed to you.

Contract addresses

One fast bridge per chain, shared by every launch: Ethereum, Base, Robinhood Chain and BSC (deployed 16 October 2026), then Solana. The full interface is the bridge’s ABI and INTERFACE.md. These are the addresses, and the endpoint ids a price sheet and a fill use:

ChainFast bridgeEndpoint idChain id
Ethereum0xE6feCfb8520Ce9417beB569ef95d6b8304802897301011
Base0x51D658a71AEa38f123d4c73e42703Fb9C949900A301848453
Robinhood Chain0xcC87BeDb95E4ac0cf20039442A964ADB390c02c4304164663
BSC0xd7C10226327451ac768512aC4A5035a9477f862a3010256
Sheet EFrom code
E.1

For integrators and AI agents

E.1

Pangea is a token launchpad. Each launch deploys its token as five contracts, a LayerZero OFT on Ethereum, Base, Robinhood Chain and BSC and a mint on Solana, and opens one liquidity pool per chain at the same US dollar price. This section is the reference for reading and using it from code.

These docs are also published as Markdown, generated from the same source as this page:

Index for language modelshttps://www.pangea.trade/llms.txt
Everything in one filehttps://www.pangea.trade/llms-full.txt
These docs as Markdownhttps://www.pangea.trade/app/docs.md

Each section is also on its own, at /app/docs/<section id>.md. This section is /app/docs/integrators.md.

The five chains

ChainName in the APIChain idEndpoint id
Ethereumethereum130101
Basebase845330184
Robinhood Chainrobinhood466330416
BSCbsc5630102
Solanasolananone30168

The endpoint id is LayerZero’s, and it is how the token names a chain. Solana has no EVM chain id.

Units

  • Every token has 9 decimals, on all five chains. An amount in base units is a whole number at 9 decimals: one token is 1,000,000,000 base units.
  • A transfer between chains crosses at 6 decimals. It is rounded down to a multiple of 1,000 base units, and the odd base units stay in the sender’s wallet.
  • Fees are in basis points. One basis point is 0.01%.
  • The standard supply is 5 billion tokens, 1 billion in each chain’s liquidity pool at launch. On an EVM chain totalSupply() starts at that chain’s 1 billion and is a reported figure that moves no balance, so every chain shows a market cap on the same basis (LayerZero routes says how it is reported); launchTotal() is the sum across the launch’s chains, and localSupply() what is on that chain now. On Solana the mint’s supply is what is on Solana now: tokens bridged in are minted there and tokens bridged out are burned. The market cap situation explains why terminals show different market caps.
  • A launch on selected chains is 1B on each of its chains only. A degen mode launch is 1B in all: each chain mints an equal share, every chain’s totalSupply() and launchTotal() report the 1B, and on Solana the mint holds the full 1B with the rest in its bridge reserve. Degen mode has the figures.
  • Times in the API are unix seconds unless a field says otherwise.

Rules that surprise people

  • Every launch is paid for by the person launching it, once, on the chain they pick, in that chain’s native asset. There are no free launches.
  • Every launch opens at the same price on every chain, at the market cap its creator chose for the whole coin, every chain together: $3,000 to $500,000 a chain times the chains (in all, for a degen mode coin), signed into the launch. The price per token is that market cap over the whole supply. Launches before factory v2 all opened at $3,000 a chain, $0.000003 a token. Read a token’s price from the API; do not assume its opening one. On factory v3 there is no locked supply: degen mode is normal mode, every chain minting its own 1B and pooling all of it, opening at $600 a chain ($3,000 for a coin on five chains). The least a v3 launch may open at is $600 a chain, which the platform may set between $600 and $3,000 a chain; standard mode still opens at $3,000 a chain unless the creator picks another cap.
  • A token has one address per chain it is on, all different (or one address on the EVM chains, for a token launched with “Same contract address on all EVM chains”), and every one of them ends in 6ea: the last three hex digits on the EVM chains, the last three characters of the mint on Solana. Read the addresses from the API. Do not assume two chains share one.
  • A transfer between chains of a token from before factory v3 costs 0.2% of the amount, kept by the token for the platform, plus LayerZero’s message fee in the source chain’s native asset. A factory v3 token's bridge fee is its chain's registry figure (0 to 0.5%), and a transfer from the chain where it is cheaper to the chain where it is dearer also pays the price-gap fee, burned at the destination: the quote's gap.burned, an estimate. It is attested after 1 confirmation on its source chain and typically arrives in 4 to 16 seconds.
  • Each launch sets its trading fee once, between 0.3% and 5%. No contract function or program instruction changes it afterwards.
  • The first transfer on a Solana route nobody has used yet also pays a one-time setup fee for that route. Every later transfer on that route does not. Any wallet can pay it, a smart-contract wallet too, except a Safe: its transactions wait for its owners, so a Safe bridges once the route is set up. Pay it from your own wallet, as the bridge page does: a payment sent through another contract (a multicall or a router) is refunded to that contract, not to you, and one made by delegatecall is not seen at all.
  • On the four EVM chains nothing can mint more of a token. On Solana the mint authority is the token's store in Pangea's deployment of LayerZero's OFT program, which mints exactly what was burned on another chain. Bridging burns on one chain exactly what it mints on another, with one exception: a factory v3 token's price-gap fee is withheld at the destination and never minted, so it is burned.
  • Prices on the five chains are separate markets. They are held together by arbitrage, not by a mechanism, and can differ.

What you can and cannot do from code

  • You can read every launch’s pools, prices, reserves and addresses, its trades and volume, recent bridge transfers and its metadata, over the HTTP endpoints below.
  • You can trade directly against the pools: Uniswap v4 on the EVM chains, Meteora DAMM v2 on Solana. The API gives each pool’s id and venue.
  • You can bridge by calling the token itself, or price a transfer over HTTP and sign the transaction it returns.
  • You can launch over the Launch API: quote a launch, pay for it from your own wallet, hand it over and follow it. The price snapshot every chain launches with is still signed by the platform, after your payment is checked. Launch API →
  • No API key is needed and there are no accounts. Every endpoint here is public. Where one is limited, the limit is per caller, by IP address. The Launch API has no rate limit for normal use, and takes an optional partner key: API keys and limits →.
  • Do not crawl the API. Each read costs reads on five chains. Call an endpoint when you need its answer; robots.txt asks crawlers to stay out of /api/.

HTTP endpoints

All are under https://www.pangea.trade/api. They take and return JSON and need no key. Over a limit the answer is 429 with a message saying how many seconds to wait. An error is { "error": "..." }. A value the chains could not give is null rather than a guess, so check for it.

EndpointWhat it returnsLimit per caller
GET /poolsEvery launch, with its pool on each chain: addresses, price, reserves, supply.None. Every caller is served one shared reading: re-read as soon as a trade moves a pool while the live page is open, and at least every 60 seconds unless the deployment sets another interval.
GET /historyTrades, volume in US dollars and price candles.30 uncached reads in ten minutes. An answer is reused for 45 seconds.
GET /bridge/activityRecent transfers between chains.None.
GET /token/metaA token’s own metadata document: description, image, links.None.
POST /bridge/quoteA transfer between chains, priced, with the transaction to sign.120 in ten minutes and 15 in any one minute.
Every public endpoint, including the ones not described here, is in API reference →
Pools, GEThttps://www.pangea.trade/api/pools

No parameters. The answer is { live, readAt, rates, blockers, launches }. When nothing has launched, live is false and launches is empty. Each launch carries:

FieldWhat it is
slug, name, symbolThe launch’s name in every other endpoint (slug), and its name and symbol.
pools[]One entry per chain, including chains that could not be read.
pools[].chainThe chain’s name in the API.
pools[].tokenThe token’s address on that chain. This is where a token’s five addresses are published.
pools[].poolId, pools[].venueThe pool’s id and the venue it is on.
pools[].price, pools[].priceUsdQuote asset per token, and the same in US dollars. quoteSymbol names the quote asset.
pools[].reserves, pools[].tokenReservesWhat the pool holds: quote asset, and whole tokens.
pools[].localSupplyWhole tokens on that chain now.
pools[].errorWhy that chain has no reading, when it has none. Set only when there is no price.
pools[].note{ kind, text }, when present: a fact about a pool whose price was read and is good, never an outage. kind is "extra-liquidity" (others added liquidity to the pool; reserves are the launch’s position only), "range-unknown", "position-unknown" or "position-mismatch" (the reserves could not be worked out).
launchTotal, launchTotalRawThe token’s total supply in whole tokens, and in base units as a string. For a perChain launch, 1B on each chain it is on; for a degen mode launch, 1B.
supplyModel, capSupply"perChain" for a launch with 1B on each chain (1B per chain · 5B across 5 chains), "global" for a degen mode launch and for launches before 2026-10-15, "per-chain" for a v4 coin (1B per opened chain; capSupply the supply across its open chains, capSupplyMax 1B times its listed chains). Market cap is a price times capSupply, which is launchTotal: the whole supply across the launch’s chains (5B for perChain on five). For a degen mode coin it is circulating: the 1B less what was burned; circulating is null for any other coin.
degentrue for a coin launched in degen mode: 1B in all, every chain reporting the 1B.
launchChains, chainsTotalThe chains the launch is on, and how many: five unless it was launched on selected chains.
cheapest, spreadUsdThe chain with the lowest US dollar price, and the gap between the highest and lowest price as a fraction.
sold, burntWhole tokens that have left the pools, and whole tokens sent to the burn addresses across chains: 0x…dEaD on each EVM chain, the incinerator's token account on Solana.
metadataURIThe token’s metadata document, as the contract names it.
History, GEThttps://www.pangea.trade/api/history

Query: launch, a slug, or leave it out for every launch; hours, a whole number from 1 to 168, 24 unless given. The answer is { readAt, hours, cursor, rates, totals, launches }. An unknown slug is a 404. Each launch carries volumeUsd, trades, coverage, chains, candles and recent.

  • coverage runs from 0 to 1. Below 1 the figures describe part of the window you asked for, not all of it. Show it, or check it.
  • candles are { t, o, h, l, c, v, vu }, one per bucket that traded, where t is the bucket's start. A bucket with no trade is left out, not filled with the last price. The prices are in US dollars. v is the quote volume in each chain's own asset, so across chains it adds ether, BNB and SOL together; vu is the bucket's volume in US dollars at the same rates as volumeUsd, and null when a trade in it had no rate.
  • recent is the last trades, newest first: { id, chain, timestamp, txHash, side, tokenAmount, quoteAmount, quoteSymbol, priceUsd, valueUsd }, where side is buy or sell.
  • A coin's whole history, a page at a time: add trades=latest (the newest page) or trades=before:<next> (the page after the one that gave next) with launch, and limit of 50, 100 or 200. The answer is { slug, readAt, trades, next }, every chain newest first, the rows as in recent; next is null on the page that reached the first trade. The first page also carries total, every trade since the launch, and complete, whether every chain is recorded back to it.
Bridge activity, GEThttps://www.pangea.trade/api/bridge/activity

Query: token, a slug, optional; hours, 1 to 168, 24 unless given; limit, 1 to 100, 25 unless given. The answer is { recording, hours, since, uncovered, totals, cursor, transfers }. A transfer is { id, guid, slug, from, to, at, amount, party, complete, tookSeconds }.

  • recording false means this deployment keeps no record, and the list is empty for that reason, not because nobody bridged. reason says why.
  • uncovered lists the chains whose sends are not recorded. A transfer out of such a chain appears as an arrival with no send.
  • complete false means one side is recorded: a transfer in flight, or one that failed. The feed does not guess which.
Token metadata, GEThttps://www.pangea.trade/api/token/meta

Query: launch, a slug, required. The answer is { uri, resolvable, name, symbol, description, image, website, twitter, telegram }, or { uri, resolvable: false, reason } when the document cannot be opened. Every field is the token author’s own claim. The name and symbol that cannot be edited are the contract’s, in the pools endpoint.

Bridge quote, POSThttps://www.pangea.trade/api/bridge/quote
The body you POST
{
  "slug": "<launch slug>",
  "from": "base",
  "to": "bsc",
  "amount": "1000000000000",
  "user": "0x<sender, and the recipient unless one is given>"
}
FieldWhat it is
slugThe launch.
from, toChain names, as in the table of chains above. They must differ.
amountBase units, as a decimal string.
userThe EVM address sending, and the recipient on an EVM chain unless one is given.
solanaUserThe Solana address, when Solana is the source or the destination.
recipientWho receives, when it is not the sender. Required for a transfer between an EVM chain and Solana: across the two a transfer is never sent to a wallet by default.

The answer is { symbol, from, to, amount, routes }, where routes holds two routes: canonical, the token’s own LayerZero transfer, and fast, the same transfer with a fast order. A route that cannot be used says why in unavailable. It carries:

FieldWhat it is
idcanonical or fast. The fast route’s own fields are in API reference.
amountOutWhat the transfer credits before a factory v3 token's price-gap fee, in base units. For a token from before factory v3 that is what the recipient receives. For a factory v3 token what arrives is amountOut less gap.burned (an estimate, in gap.decimals; every v3 token has 9 decimals on every chain) and, into Solana, less gap.delivery.amount too (our delivery key's cost there), and never less than gap.minCredited: below it the arrival waits at the destination. Never build your own minimum from amountOut alone.
gapA factory v3 token only: the price-gap estimate for this transfer. state is estimate, free (nothing withheld), held (the destination cannot price it yet: it waits there) or unavailable (no standard route now; ask again); burned what the destination withholds, minCredited the least it may credit (gapToleranceBps under the estimate), and minCreditedSource that minimum in the source's decimals, out of Solana.
nativeFeeWhat it costs in the source chain’s native asset, in its smallest unit. Send it as the transaction’s value.
tokenFee, platformFeeEverything taken in the token, and the platform’s part of it, in base units. Both are the token’s fee.
secondsThe estimated time.
callThe transaction to sign: { to, data, value, chainId }. Null when the transfer cannot be built as one contract call, as out of Solana.
setupFeePresent when this transfer opens a Solana route: the part of nativeFee that pays for it, once.
unavailablePresent when the route cannot be used, with the reason.

Bridging from code

On an EVM chain the token is a LayerZero OFT and you call it directly. Ask quoteOFT what a transfer debits and delivers, name the delivered amount as the minimum, ask quoteSend for the message fee, and send that fee as the transaction’s value. Leave extraOptions empty: the gas the destination needs is already enforced by the token.

On the token, on the chain the transfer leaves
struct SendParam {
    uint32 dstEid;       // the destination's endpoint id
    bytes32 to;          // the recipient, left-padded to 32 bytes
    uint256 amountLD;    // base units
    uint256 minAmountLD; // the least that may arrive
    bytes extraOptions;
    bytes composeMsg;
    bytes oftCmd;
}
struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }
struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }

function quoteOFT(SendParam calldata _sendParam)
    external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken)
    external view returns (MessagingFee memory);
function send(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress)
    external payable returns (MessagingReceipt memory, OFTReceipt memory);

event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD);
event OFTReceived(bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD);

error AmountTooSmallToBridge(uint256 amount);
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
Read on the tokenWhat it returns
BRIDGE_FEE_BPS()A token from before factory v3: its bridge fee in basis points, 20. A factory v3 token's fee is its chain's registry figure (0 to 50): take what arrives from quoteOFT.
bridgeFeeTo()Where that fee goes on this chain.
peers(uint32 eid)The token’s counterpart on another chain. Zero means there is no route to it.
decimals()9, on every chain.
totalSupply()The reported supply: this chain’s 1B slice, or, for a launch with a SupplyReporter, the token’s supply on Solana as the chain’s SupplyReporter mirrors it (on a factory v1 token, never below localSupply() or above launchTotal()). For a degen mode launch, the 1B total, which never changes.
launchTotal(), launchSlice()The whole launch’s supply across every chain, and this chain’s slice of it.
supplyReporter()The only address that may report this token’s supply. Launches made before reporting existed have no such function.
localSupply()What is on this chain now.
metadataURI()The token’s metadata document.

On Solana the token is an account of LayerZero’s OFT program, SNyKyiZYU4R54SF9sYLmjCaCUsa1sC6QouNAj3x3Zzf, and a transfer out of Solana is an instruction to that program, not a contract call. Into Solana the recipient is the wallet that will own the tokens.

Launches on chain

On each EVM chain the launch factory emits one event per launch. The current factories’ addresses are in LayerZero routes.

On each EVM chain’s launch factory
event Launched(
    bytes32 indexed launchId,
    address indexed token,
    address splitter,
    address launchOwner,
    address locker,
    uint128 liquidity,
    uint256 dust
);

Addresses

  • A token’s five addresses are in the pools endpoint, at launches[].pools[].token, and on the token’s page.
  • The current launch factories, and each chain’s LayerZero endpoint, libraries, executor and verifiers, are in LayerZero routes.
E.2

Bridging from code

E.2

On the four EVM chains you call the token itself: it is a LayerZero OFT. Out of Solana the send is an instruction to LayerZero’s OFT program, and the simplest way to build it is to ask Pangea’s API for the unsigned transaction. The signatures and events are in For integrators; the routes they travel are in LayerZero routes.

  • amountLD is in base units, nine decimals. The token keeps 0.2% of it (a factory v3 token: its chain's registry figure, at most 0.5%), rounded down, and what crosses is rounded down to six decimals. quoteOFT returns both figures, exactly.
  • minAmountLD is the least that may arrive, or the send reverts with SlippageExceeded. On a token from before factory v3 the fee is fixed, so the received amount from quoteOFT can be used as it is; on a factory v3 token the fee is read at every send, so ask quoteOFT right before you send.
  • extraOptions stays empty. The token adds its own gas floor to every send; anything you add is added on top and paid for by you.
  • A factory v3 token: send the minimum. Its oftCmd must carry your least credited after the price-gap fee: abi.encode(uint256 minCreditedLD) in the source's base units (the low 248 bits; the top byte is flags, 0). With an empty oftCmd the destination credits whatever the gap leaves. Take it from the quote's gap object (gap.minCredited, destination units; gap.minCreditedSource out of Solana), set by gapToleranceBps (default 100, at most 2000). Its bridge fee is its chain's registry figure, 0 to 0.5%: read what arrives from quoteOFT.
  • to is 32 bytes: an EVM address left-padded with zeros, or a Solana wallet’s public key. Never a Solana token account.
  • quoteSend gives LayerZero’s fee for that exact SendParam. Send it as the transaction’s value, with payInLzToken false. Any excess goes back to the refund address.
  • No approval is needed for a standard send: the token burns from your own balance.

EVM to EVM, with viem

Quote a transfer of 1,000 PTEST4 from Base to BSC
import { createPublicClient, http, parseAbi, pad } from "viem";
import { base } from "viem/chains";

const OFT = parseAbi([
  "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
  "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
  "struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
  "struct OFTFeeDetail { int256 feeAmountLD; string description; }",
  "struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }",
  "struct MessagingReceipt { bytes32 guid; uint64 nonce; MessagingFee fee; }",
  "function quoteOFT(SendParam _sendParam) view returns (OFTLimit, OFTFeeDetail[], OFTReceipt)",
  "function quoteSend(SendParam _sendParam, bool _payInLzToken) view returns (MessagingFee)",
  "function send(SendParam _sendParam, MessagingFee _fee, address _refundAddress) payable returns (MessagingReceipt, OFTReceipt)",
]);

const TOKEN = "0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea"; // PTEST4 on Base
const BSC = 30102;
const recipient = "0x000000000000000000000000000000000000bEEF"; // who receives on BSC
const amount = 1_000n * 10n ** 9n; // 1,000 tokens: nine decimals

const client = createPublicClient({ chain: base, transport: http(process.env.BASE_RPC) });

// What the transfer debits and what arrives: 0.2% stays with the token, the rest crosses.
const sendParam = {
  dstEid: BSC,
  to: pad(recipient), // an EVM address, left-padded to 32 bytes
  amountLD: amount,
  minAmountLD: 0n,
  extraOptions: "0x", // empty: the token already enforces the gas the destination needs
  composeMsg: "0x",
  oftCmd: "0x",
};
const [, , receipt] = await client.readContract({ address: TOKEN, abi: OFT, functionName: "quoteOFT", args: [sendParam] });
sendParam.minAmountLD = receipt.amountReceivedLD; // the least that may arrive

// LayerZero's fee for this exact transfer, in ETH.
const fee = await client.readContract({ address: TOKEN, abi: OFT, functionName: "quoteSend", args: [sendParam, false] });
console.log({ sent: receipt.amountSentLD, arrives: receipt.amountReceivedLD, nativeFee: fee.nativeFee });

// The send, from the holder's wallet, paying the fee as the value:
// await wallet.writeContract({ address: TOKEN, abi: OFT, functionName: "send",
//   args: [sendParam, fee, account.address], value: fee.nativeFee });

Run against Base on 19 October 2026, it printed sent: 1000000000000n, arrives: 998000000000n, nativeFee: 109546395808232n: 1,000 tokens sent, 998 arrive, and about 0.00011 ETH for LayerZero.

The same, with cast

quoteOFT and quoteSend from the command line
TOKEN=0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
RPC=your-base-rpc-endpoint
TO=$(cast pad --len 32 0x000000000000000000000000000000000000bEEF)

# what is sent and what arrives: (limit), (fee details), (sent, received)
cast call $TOKEN "quoteOFT((uint32,bytes32,uint256,uint256,bytes,bytes,bytes))((uint256,uint256),(int256,string)[],(uint256,uint256))" \
  "(30102,$TO,1000000000000,0,0x,0x,0x)" --rpc-url $RPC

# LayerZero's fee in wei: (nativeFee, lzTokenFee)
cast call $TOKEN "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \
  "(30102,$TO,1000000000000,998000000000,0x,0x,0x)" false --rpc-url $RPC
Pad an address with cast pad, which pads on the left. cast to-bytes32 pads on the right, and a send to that would go to a different address.

EVM to Solana

The same send, with dstEid 30168 and the recipient’s Solana wallet as the 32 bytes. The tokens arrive in that wallet’s token account, created on delivery; its rent, 2,074,080 lamports, is in every transfer’s gas floor and so in quoteSend’s fee.

Quote a transfer from Base to Solana, and check the route is open
import { createPublicClient, http, parseAbi, toHex } from "viem";
import { base } from "viem/chains";
import { Connection, PublicKey } from "@solana/web3.js";

const OFT = parseAbi([
  "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
  "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
  "struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
  "struct OFTFeeDetail { int256 feeAmountLD; string description; }",
  "struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }",
  "function quoteOFT(SendParam _sendParam) view returns (OFTLimit, OFTFeeDetail[], OFTReceipt)",
  "function quoteSend(SendParam _sendParam, bool _payInLzToken) view returns (MessagingFee)",
  "function peers(uint32 eid) view returns (bytes32)",
]);

const TOKEN = "0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea"; // PTEST4 on Base
const BASE_EID = 30184;
const SOLANA = 30168;
const owner = new PublicKey("<the Solana wallet that will own the tokens>");
const amount = 1_000n * 10n ** 9n;

const evm = createPublicClient({ chain: base, transport: http(process.env.BASE_RPC) });
const solana = new Connection(process.env.SOLANA_RPC, "confirmed");

// Is this token's route from Base open on Solana? It is once LayerZero's receive config exists.
const peer = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "peers", args: [SOLANA] });
const store = new PublicKey(Buffer.from(peer.slice(2), "hex")); // the OFT store
const eid = Buffer.alloc(4);
eid.writeUInt32BE(BASE_EID);
const ULN = new PublicKey("7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH");
const [receiveConfig] = PublicKey.findProgramAddressSync([Buffer.from("ReceiveConfig"), eid, store.toBuffer()], ULN);
const open = (await solana.getAccountInfo(receiveConfig)) !== null;

const sendParam = {
  dstEid: SOLANA,
  to: toHex(owner.toBytes()), // the owner's public key as 32 bytes, not a token account
  amountLD: amount,
  minAmountLD: 0n,
  extraOptions: "0x", // the token adds the gas floor and the rent of the token account
  composeMsg: "0x",
  oftCmd: "0x",
};
const [, , receipt] = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "quoteOFT", args: [sendParam] });
sendParam.minAmountLD = receipt.amountReceivedLD;
const fee = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "quoteSend", args: [sendParam, false] });
console.log({ store: store.toBase58(), routeOpen: open, arrives: receipt.amountReceivedLD, nativeFee: fee.nativeFee });
// Then send exactly as from EVM to EVM. With routeOpen false, the transfer waits on Solana
// until the route is opened: see the route setup below.

If the token’s route from that chain is not open on Solana yet, a transfer sent now waits there until it is. To open it as part of your transfer, as the Bridge page does, POST /api/bridge/quote returns the setup payment in routePayment: send it first, then report it to POST /api/bridge/route-open, which opens the route or refunds the payment, and then send the transfer. API reference →

Solana to EVM, with @solana/web3.js

Out of Solana the send is LayerZero’s OFT program’s own instruction, with about thirty accounts and a lookup table. POST /api/bridge/solana-tx builds it, unsigned, for your wallet to sign: it names the accounts, sets the minimum to what arrives, and adds the route’s opening when a route is used for the first time. You pay LayerZero’s fee and the transaction fee in SOL; the token’s 0.2% is kept in the OFT store’s escrow for the platform.

Price and build a transfer from Solana to Base, then simulate it
import { Connection, VersionedTransaction } from "@solana/web3.js";

const API = "https://www.pangea.trade/api";
const sender = "<the Solana wallet holding the tokens>";
const recipient = "0x000000000000000000000000000000000000bEEF"; // who receives on Base
const amount = (1_000n * 10n ** 9n).toString(); // 1,000 tokens, in base units

const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));

// The price: what arrives on Base, and LayerZero's fee in lamports.
const quote = await post("/bridge/quote", { slug: "ptest4", from: "solana", to: "base", amount, user: recipient, recipient, solanaUser: sender });
const standard = quote.body.routes?.find((r) => r.id === "canonical");
if (!standard || standard.unavailable) throw new Error(standard?.unavailable ?? quote.body.error);
// A factory v3 token: what arrives is amountOut less the gap estimate, never less than gap.minCredited.
const gap = standard.gap ?? null;
console.log({ credited: standard.amountOut, gapBurned: gap?.burned, nativeFee: standard.nativeFee, setupFee: standard.setupFee ?? null });

// The unsigned transaction: the OFT send, and the route's opening if it is not open yet.
// A factory v3 token's send must name its minimum (minCredited, Solana base units): without one it is refused (400).
const ask = { slug: "ptest4", to: "base", amount, sender, recipient, ...(gap ? { minCredited: gap.minCreditedSource } : {}) };
let built = await post("/bridge/solana-tx", ask);
// 202 { pending: true }: a price record or a route opening is still landing -- ask again in a moment.
while (built.status === 202) {
  await new Promise((r) => setTimeout(r, 2_000));
  built = await post("/bridge/solana-tx", ask);
}
// stage "record" (a v3 token with no recent price on record): sign and send it, then ask again with
// { ...ask, recorded: <its signature> }; the answer is then the send.
if (built.status !== 200) throw new Error(built.body.error ?? `stage ${built.body.stage}`);
const tx = VersionedTransaction.deserialize(Buffer.from(built.body.transaction, "base64"));

// Check it before signing: a simulation sends nothing.
const connection = new Connection(process.env.SOLANA_RPC, "confirmed");
const sim = await connection.simulateTransaction(tx, { sigVerify: false });
console.log({ stage: built.body.stage, received: built.body.received, totalFee: built.body.totalFee, error: sim.value.err });

// Sign with the sender's wallet and send:
// const signed = await wallet.signTransaction(tx);
// await connection.sendRawTransaction(signed.serialize());

Run read-only on 19 October 2026 from a Solana holder, it built a transaction at stage send with received: 998000000000 and a fee of at most 636,478 lamports, and the simulation passed. The stages are send, the transfer alone; together, the route’s opening and the transfer in one transaction; and setup, the opening alone, after which you ask again with after set to its signature. A factory v3 token adds record: the price record its send needs first (its signature fee only), after which you ask again with recorded set to its signature. A 202 with pending: true means the step before is still landing: ask again in a moment. A v3 token's send also needs minCredited (the quote's gap.minCreditedSource), or it is refused with a 400.

After the send

  • The send emits OFTSent(guid, ...) on the source chain and the delivery OFTReceived(guid, ...) on the destination. The guid is the transfer’s id on LayerZero Scan.
  • Look the guid or the transaction up on LayerZero Scan to follow it, or poll the destination for OFTReceived with that guid.
  • Delivery needs nothing from you. If a message sits verified and undelivered, anyone may deliver it, except a factory v3 token's charged arrival into Solana, which only Pangea's appointed deliverers may land.
E.3

API reference

E.3

All endpoints are under https://www.pangea.trade/api, take and return JSON, and need no key or account. The general rules are in For integrators, which also describes five of them in full: pools, history, bridge activity, token metadata and the bridge quote. The relayers’ price board is in Relayers. This section lists all of them and describes the rest.

  • An error is { "error": "..." } with a 4xx status (a 5xx is a fault, and its body may be a proxy's own). A value the chains could not give is null, not a guess.
  • Limits are per caller, by IP address, counted on each server. Over a limit the answer is 429 and the message says how many seconds to wait; some also send Retry-After.
  • A JSON body that is too large is 413, one that is compressed is 415, one that does not arrive within 10 seconds is 408, and one that is not a JSON object of the expected fields is 400.
  • Call them from a server or a script. Only the supply endpoints (/supply/[token] and its two plain-number forms), /gap-price/[id] and /gap-price/quote-usd send Access-Control-Allow-Origin: *; the rest send no CORS header, so a web page on another site cannot read them from the visitor’s browser.
  • Answers are cached briefly in front of the app (most for 5 to 60 seconds, as each endpoint says), so polling faster than that returns the same answer.
  • Amounts are base units as decimal strings. Chains are named ethereum, base, robinhood, bsc and solana. A launch is named by its slug.
  • Three endpoints answer as a stream of server-sent events: data: <json> lines, with content-type: text/event-stream. Read them with a streaming fetch; they are POST, so an EventSource cannot open them. The Launch API’s event stream and the launches stream are GETs, in the same format with named events.
  • A coin is named by its slug, its launch id or any chain’s token address wherever the path says [id]. The coin, its supply, the pools, the holders and the launches feed carry degen and the coin’s supply: circulating, the bridge reserve and where it is held, the total, and what each chain mints and each token reports.

Every public endpoint

EndpointWhat it is forLimit per caller
GET /poolsEvery launch and its pool on each chain.None; one shared reading.
GET /historyTrades, volume and price candles.30 in ten minutes, uncached reads only.
GET /holdersWho holds a token, per chain.60 in ten minutes, uncached reads only.
GET /gap-price/[id]A shared-bridge coin’s signed gap price for one transfer: the coin id or its token, src and dst (chain id or name, several dst comma-separated), and optionally amount (the send in the source token’s base units). Pass the answer unchanged in the bridge call. 503 means no signed price right now: send without one (the chain’s own readers price it, or no gap fee). Not cached.20 at once, then 1 a second; all callers 50 a second.
GET /gap-price/quote-usdThe signed USD price of a coin’s native quote on one EVM chain, for a buy-and-bridge that carries its own quote price (buyAndSendQuoted): chain (chain id or name) and optionally quote (0x00..00, the native coin, is the only one signed). Pass signedQuote unchanged in the call, and fetch it right before: it is valid for about 30 seconds. 503 means no price right now; on Robinhood a buy then waits, since that chain has no other quote price. 404 for an ERC-20 quote. Not cached.Shared with the gap price: 20 at once, then 1 a second; all callers 50 a second.
GET /token/metaA token’s metadata document.None.
GET, POST /token/socialsWho may edit a coin’s socials (its creator, EVM and Solana); with history=1 every version so far, each with the creator’s signature, and with onchain=1 each EVM chain’s metadata authority and URI; POST stores a new version the creator signed (EIP-712, or a signed message on Solana) with a new website, X, Telegram, Discord, description or logo. No transaction and no gas: /token/meta shows the newest version, marked as edited by the creator.GET 120 a minute, 10 with onchain=1. POST 30 attempts in ten minutes; 10 stored edits an hour, and 6 an hour and 24 a day per coin.
GET /logo/[cid]/[file]A token’s logo from this site, as square WebP: the file is 64.webp, 128.webp or 256.webp, and the CID is the image’s own, as /token/meta names it. Cached for a year: a CID’s picture cannot change.None.
GET /platform-fundsWhat is still to be spent on PANGEA buybacks on each chain: the buyback wallets’ balance less what they keep for gas, plus their share of platform fees not yet forwarded, in native units and US dollars, with the total and the buyback wallets.None; one shared reading a minute.
GET /creator-rewardsEverything launches’ trading fees have paid to their creators, all time: per chain in native units and US dollars at today’s prices, with the total and which chains are not counted in full.None; one shared reading a minute.
GET /holder-payoutsCreator fees paid to holders by the coins whose fees go to a tokenomics vault: all time (every vault the coin’s fees have gone to, earlier ones included) and the last 24 hours, by chain, by coin and by reward token, as each vault’s own counters on chain say, in US dollars at today’s prices.None; one shared reading a minute.
GET /revenuePlatform and creator fee payouts over any span (from, to), in US dollars at each payout’s own time, per chain and asset.None; held 30 seconds.
GET /buyback-burnsEvery PANGEA buyback so far, with its burn transaction: totals per chain, the latest 50, or every one a page at a time.None; held 15 seconds.
GET /ctoThe coins under a community takeover, and when each chain’s fee stream moved.120 a minute.
GET /stock-pairsThe coins with a stock-paired chain, and the stock on each.120 a minute.
GET, POST /token/fee-nativeWhether a coin’s creator fees in its pair token (any pair but the chain’s own coin) are paid in the chain’s native coin; POST switches it, signed by the creator.GET 120 a minute; POST 20 in ten minutes.
GET /tokenomicsOne chain’s tokenomics vault, read from the chain: its split, what waits in each bucket, and what it has burned, bought and added.60 a minute.
GET /tokenomics/v2/burnA tokenomics v2.2 vault’s burn tokens: the other tokens it buys to burn, and how much of each so far.30 a minute.
GET /tokenomics/v2/holderWhat one holder is owed and has been paid by a coin’s vault, per reward token.30 a minute.
POST /tokenomics/v4/switchWhere v4 is switched on: a v4 coin’s tokenomics on one chain, built for your own wallet to sign; nothing is signed or sent here. op: "enable" (with vault) turns it on or re-points it, "disable" turns it off, "clear" clears a wrongly pinned vault. The creator may only turn it on, while it is off and the role is not renounced; the platform admin may turn it on, off and re-point it at any time (EVM adminEnableTokenomics; Solana admin_enable_tokenomics, on or re-point). For a Solana-home coin, "push" builds the creator role push from Solana, "role" reads it back, and "authorize" builds the open authorisation from Solana (pusher, opener, deadline).20 a minute.
GET /v4/abiThe full ABI of every v4 EVM contract and the IDL of every v4 Solana program, as JSON: all of them, or one with ?id= (and &kind=idl for a program), or the list with each file’s sha256 with ?manifest=1; add &download=1 to save it as a file.None.
GET /v4/contractsEvery v4 address the deploy recorded, per chain: its chain id, pcid and contracts by name (EVM addresses checksummed, Solana programs and accounts in base58). Before the first deploy it answers published: false. The top level is the first v4 set (v4a); sets.v4b is the second set (the redeploy new coins launch on), with its own addresses, once it is deployed.None.
POST /tokenomics/v4/accountWhere v4 full scope is switched on (NEXT_PUBLIC_GAP_V4 and NEXT_PUBLIC_GAP_V4_FULL both on; 404 otherwise): what is at one Solana tokenomics account (address): whether it exists, its owner program and its data, read before a creator’s tokenomics setup is built, so a retry reuses the account. Reads only.60 in ten minutes.
GET /coin/[id]/fee-receivers/pendingA fee receiver change waiting to be accepted, per chain.60 a minute.
GET /bridge/healthWhether bridging is working: ok, or the problems.None.
POST /cosignA v3 transfer too large for one part, co-signed by Pangea’s delivery service, with its minimum credited.6 a minute, 30 in ten minutes.
GET /trade/tx-statusWhether an EVM transaction succeeded, failed or is pending.90 a minute.
GET /bridge/activityRecent transfers between chains.None.
GET /bridge/balanceOne address’s balance of a token or the native asset.120 in ten minutes
POST /bridge/quoteA transfer priced, both lanes, with the transaction to sign.120 in ten minutes, 15 a minute.
POST /bridge/solana-txThe unsigned transaction for a transfer out of Solana.60 in ten minutes, 10 a minute.
POST /bridge/solana-fast-txThe same, as a fast order.60 in ten minutes, 10 a minute.
POST /bridge/route-openOpens a Solana route once its setup payment has landed.120 in ten minutes
POST /bridge/arrivalA stream that says when a transfer has arrived.60 in ten minutes
POST /bridge/v4/arrivalVersion-4 coins, where this site has them (404 otherwise): POST { slug, from, to, tx, guid? }, and each transfer of that source transaction toward to comes back landed, held below its minimum (the recipient can accept it at the credit it would get now), accepted, waiting for a Solana price record, or on its way, with the amounts in the token’s own decimals. Reads only; nothing is signed.120 in ten minutes
GET /bridge/v4/historyVersion-4 coins, where this site has them (404 otherwise): ?slug= (and &wallet= for one wallet’s) the coin’s recent v4 transfers, each with what was sent, what was credited and what was burned on arrival, and the accepts, from the tokens’ own events; a chain that did not answer in time is named in unread, never left out silently.120 in ten minutes
GET /bridge/v4/pathA version-4 coin’s stored dollar price path on one chain, where this site has version 4 (404 otherwise): ?slug=&chain=, read from the chain now: none (its pool pairs with the native coin or a dollar token), ok, weak (thin: the dollars a 2% move takes on its thinnest pool, unmeasured, or a pool keeping a single price observation), depth not measured (Solana: the route reads, its depth is not measured yet) or does not read (arrivals priced by it wait or burn until it reads again or someone replaces it), with the quote’s decimals, and whether the path was replaced since launch (anyone can put the launch path back whenever it reads). The path judged is the one the coin prices by, as stored at launch or as replaced since.120 in ten minutes
POST /launch/v4/buy-listA version-4 launch’s Solana list (launch buys on Solana), from this site’s launch page only: op read (the list and its money account as on chain), op blockhash (for the list’s own creator or payer), op band (whether SOL is still within the band the list was quoted at, and whether the launcher’s pool fee and OFT version are still the ones the list sealed). Nothing is built or signed here: the creator’s wallet signs the list.600 in ten minutes
GET /launch/v4/pair-checkA version-4 launch’s pair token, where this site has version 4 (404 otherwise): ?chain=&address=, and what it is before payment: refused only when it truly cannot work (and why: no token code, unreadable decimals or more than the pair maximum, a token that cannot sit in a pool such as NonTransferable or an active transfer hook), not open yet on that side, or ready. Any other token is allowed, at the creator’s risk: a missing or thin pool path to a dollar price is the “weak price path” mark, never a refusal; its own decimals, its transfer tax, and that mark when the price path its launch would use is missing, thin or unmeasured (on EVM: the dollars a 2% move takes on that path’s thinnest pool). On EVM the quote itself measures the pools again and launches with the deepest path; when a pool that could be deeper cannot be measured just now it refuses (“the pools could not be measured just now, try again”) rather than pick around it, and the quote names the path it chose. A path with a pool whose price read comes within 10,000 gas of its venue’s gas cap is not used (a touch could make it stop reading). No pair list exists: nothing here allows or refuses a token by name.60 in ten minutes
POST /launch/v4/self-openA version-4 launch whose pay chain you launch from your own wallet (no Solana leg), where this site has version 4 (404 otherwise): { launchId, chain }, once your turn has come. It builds, and checks, the transactions your fee wallet sends, in order: any “keep two price observations” step a pool the coin’s price rests on still needs (anyone may send it), then the launch itself through that chain’s self-launch opening router, with what the opening owes priced now (a few base units of the pair token, bought with native and paid to the router). Nothing is sent from here, and the router takes the launch only from your fee wallet, with the one signature per chain you gave before paying (it names that chain’s router). If your time runs out, our launcher sends it: same price, same fee.30 in ten minutes
POST /launch/v4/registeredA shared-bridge coin you opened yourself, where this site has the shared-bridge self launch (404 otherwise): { chain, txHash } of the open, once it is mined. This site reads that transaction from the chain itself and lists the coin from what the chain says (its id, home, chain list and transports, its token); nothing you send is stored. The first listing of a coin is kept.20 in ten minutes
POST /launch/v4/sol-openThe Solana side of a shared-bridge coin you open yourself, where this site has the shared-bridge self launch (404 otherwise): { payer }, and the answer is a recent blockhash and the shared lookup table’s addresses, so your wallet can build and sign the open. Nothing is built, signed or sent here.60 in ten minutes
POST /launch/v4/sol-heldOpening Solana later with a wallet the coin’s home chain authorised, where this site has the shared-bridge self launch (404 otherwise): { coinId, homePcid }, and the answer is the bridge’s authorisation slot for the coin on Solana (its address, and its data or null when no authorisation has arrived yet). Your page checks it (the opener must be your wallet, before its deadline) and builds the open itself. Nothing is built, signed or sent here.30 in ten minutes
POST /bridge/refundWhether a fast order is held, and the transaction that sends it back.60 in ten minutes
POST /bridge/ordersTells the board a fast order was posted at a relayer’s price.60 in ten minutes
GET, POST /bridge/relayer-quotesThe relayers’ price board.300 in ten minutes reads, 120 publishes.
POST /chain/txA stream that says when a transaction is in a block.60 in ten minutes
POST /trade/quoteA buy or sell priced through Relay, or Jupiter on Solana.60 a minute.
POST /trade/solana-txCompiles a swap’s Solana instructions into an unsigned transaction.60 in ten minutes
GET /token/launch-buysWho bought in a launch’s own block, per chain, and whether that block has been read.60 in ten minutes, uncached reads only.
GET /coin/[id]One coin: its addresses, degen mode, its supply, and its tokenomics config as its launch signed it. No chain is read.120 a minute.
GET /supply/[token]A coin’s total, circulating and locked supply, per chain and in all, in whole tokens (locked is always 0: every launch pools all of each chain’s share). perChain.supply is the slice the launch minted on that chain, not what is on it after bridging. For a v3 coin, what its bridges burned (the gap fee: a cheap-to-dear bridge withholds part and never mints it) comes off circulating, and off the total too unless the coin's total is fixed (degen); gapBurned says how much. For CoinGecko and CoinMarketCap. A chain that cannot be read is a 503, never a smaller figure.120 a minute.
GET /supply/[token]/totalThe total supply alone, as a bare number (text/plain).120 a minute, shared.
GET /supply/[token]/circulatingThe circulating supply alone, as a bare number (text/plain).120 a minute, shared.
GET /coin/[id]/supplyA coin’s supply: what its launch made, and for a degen mode coin the same read from the chains.60 a minute.
GET /coin/[id]/supply/totalOnly the coin’s total supply, as plain text in whole tokens (the form CoinMarketCap and CoinGecko ask for): every chain’s supply counted once, less what was burned. Held a minute; 503 when the chains cannot be read and no recent figure is held.60 a minute.
GET /coin/[id]/supply/circulatingOnly the coin’s circulating supply, the same way: the total less anything locked or out of public hands (the Solana bridge reserve is never counted, as it backs the other chains).60 a minute.
GET /coin/[id]/tokenomicsA coin’s tokenomics config, and each chain’s vault read from the chain: what waits in each bucket, and what was burned, bought and added to liquidity.20 a minute.
GET /coin/[id]/vault-summaryWhat a coin’s tokenomics vault has done, all time per coin: every vault the coin’s fees have gone to on each chain, earlier ones included (each listed in previousVaults), summed per option in US dollars at today’s prices; an earlier vault not read makes the answer partial and names its chain. 404 for a coin whose fees go to no vault.30 a minute.
GET /coin/[id]/fee-receiversWho a coin’s creator fees are paid to now, read from the chains: each EVM chain’s fee splitter and the Solana launch record, with what each receiver is (a wallet, a tokenomics vault, the burn address or another contract; for a community takeover whose fees are bought back and burned, “buyback”, with no address). Cached for an hour.60 a minute.
POST /v1/launch/prepareA launch quoted, with the ticket every later step takes.120 in ten minutes per address; none with an API key.
POST /v1/launch/authorizationThe permission a creator wallet signs for another launching wallet.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/payThe transaction that pays the launch fee.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/submitHands a paid launch to Pangea’s launcher.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
GET /v1/launch/statusWhere a launch stands, chain by chain.No rate limit; the flood ceiling.
GET /v1/launch/eventsThe same, as a stream of events.3 open at once per address.
GET /v1/launchesEvery coin launched here, newest first, a page at a time.No rate limit; the flood ceiling.
GET /v1/launches/streamEvery new launch the moment it is live, as server-sent events.3 open at once per address.
POST /v1/launch/kitWhat you need to make your own launch-time buys.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/bundle-kitWhere offered: your own launch call on the chain you paid on (Ethereum or BSC), to send first in your own bundle within the window. A v4 launch asks for its kit in the sponsored prepare instead (bundleKitHeld): each Ethereum or BSC chain it opens other than the one you pay on is held for your bundle.120 in ten minutes per address.
POST /v1/launch/solana-self-callYour Solana launch call, when you send Solana’s launch from your own wallet (selfSolana).40 in ten minutes per address (90 for the landed-signature hint).
POST /v1/launch/solana-cosignedNot available yet: answers 409.None.
POST /v1/launch/v4/sponsored/prepareWhere v4 is switched on: a platform-paid v4 launch quoted, on the current v4 contract set, v4b (its Solana leg on the v4b bridge, whose open authorisation the creator's wallet signs as its hex text), with its v4 ticket; its launch buys are asked for here (the v4 launch kit). Payout wallets, one per chain type opened now: creatorEvm (always: the pay chain is an EVM chain) and creatorSvm when Solana opens now (required; a Solana chain only listed is asked when it is opened later). A v4 quote at /v1/launch/prepare answers 409 and names this route.120 in ten minutes per address, with prepare; none with an API key.
POST /v1/launch/v4/sponsored/submitWhere v4 is switched on: hands a paid v4 launch over, with every launch-buy and open signature, each checked against its ticket.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
GET /v1/launch/v4/sponsored/readinessWhere v4 is switched on: which chains a platform-paid v4 launch can open now.No rate limit; the flood ceiling.
POST /v1/launch/v4/openWhere v4 is switched on: the creator's own v4 leg of a queued launch, on its pay chain, priced now: { launchId, chain } answers { pre, tx, owed, quote, via, takeoverAt } for the creator's wallet to send before takeoverAt. Not an open-later: that is POST /v1/launch/v4/open-later.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/open-laterWhere the full-scope switch is on: opens another listed chain of a coin already launched, at any time, on the coin's own contract set (a v4a coin stays on v4a): { id, chain, step: "authorize" } by the current creator on the home chain (with the payout wallet first when that chain type has none), then { id, chain, step: "open" } by the authorised wallet; each answers the transactions for your own wallet.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/registeredWhere the full-scope switch is on: lists a mined v4 open from the chain itself: { chain, txHash }, or { chain: "solana", coinId }; answers { slug, coinId }, 422 NOT_LISTABLE when the site will not list its name or symbol.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/tokenomicsWhere the full-scope switch is on: the same as POST /tokenomics/v4/switch (on, off, clear; the role push from the coin's home chain, EVM or Solana, and its read-back; for a Solana-home coin the open authorisation), built for your own wallet.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/handoverWhere the full-scope switch is on: the creator handover (op: "transfer" with newEvm and optional newSvm, or "renounce") on an EVM-home coin's home chain, for the current creator's wallet. A Solana-home coin answers 409: its handover is signed on the coin page.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/sol-kitWhere v4 is switched on: the Solana bundle kit of a platform-paid v4 launch. With your v4 ticket: the signed Solana launch while it is held for you; with your bundle (that launch first, then 1 to 4 signed buyer transactions, the last paying the Jito tip): checked and sent to Jito as one bundle. Refused by the relay: the launch goes alone, with its launch buys.300 in ten minutes per address with authorization, pay, submit and kit together, and 120 in ten minutes per IP; none with an API key.
The launch endpoints under /v1/launch are described in Launch API →

GET /coin/{id}

Coin, GEThttps://www.pangea.trade/api/coin/{slug, launch id or token address}

One coin as the site knows it, with no chain read: { slug, name, symbol, launchId, listedAt, launchChains, chains, degen, factoryGen, supplyMode, factoryV3, supply, tokenomics, tokenomicsKnown }. chains is each chain’s { token, pool, quote }; quote is the asset its pool pairs with: "native", or { address, ticker, symbol, issuer, known } for a coin paired with a tokenized stock on that chain, found by the stock token's address (known: false for an asset the stocks table does not list). A stock-paired EVM chain can show pool: null until its pool is listed. factoryGen is 1 or 2; factoryV3: true marks a factory v3 coin (absent before v3); supplyMode is factory v2’s, 0 (1B on each chain) or 1 (degen mode), and null on a factory v1 coin. tokenomics is the config the launch’s quote signed, or null for none; tokenomicsKnown: false means it could not be read, so null is then not “none”. Errors: 400 for an id that cannot be a coin’s, 404 for one not launched here.

FieldWhat it is
degentrue for a coin launched in degen mode: 1B in all, an equal share minted and paired on each chain, every chain’s token reporting the 1B. Degen mode →
supply.totalThe launch’s whole supply, in whole tokens, as a string: 1B for degen mode (999,999,999 on three chains, whose equal shares are whole tokens), 1B a chain otherwise.
supply.circulatingWhat the chains minted and paired, in all: 1B in degen mode. The bridge reserve backs the other chains and is not extra supply, so it is never taken off circulating; /coin/{id}/supply’s live takes off what was burned and reads the bridge reserve as it is now.
supply.reserve, supply.reserveChainThe rest of a degen mode coin’s supply, held in Solana’s bridge reserve, and solana. 0 and null without one.
supply.perChainWhat each chain minted and paired, by chain.
supply.reportedPerChainWhat every chain’s totalSupply() reports in degen mode, the 1B; null otherwise, where each chain reports its own.
supply.circulatingShareBpsIn degen mode, what one chain mints as a share of the total, in basis points: 2,000 on five chains. 10,000 where each chain holds a whole supply of its own; null where the supply figures are.
supply.supplyModelperChain for 1B on each chain, per-chain for a v4 coin (1B per opened chain), global for one supply (degen mode, and launches before 2026-10-15, whose figures are null here: /pools reads them off the token).
tokenomics.perChainEach chain’s split by bucket, in basis points: { burn, buyback, liquidity, creator, community }, adding up to 10,000.
tokenomics.vault, tokenomics.ownerThe coin’s vault on the EVM chains, the fee receiver its trading fees are paid to, and its owner, the creator, who receives the creator share.
tokenomics.treasury, tokenomics.communityThe wallets the bought-and-held coins and the community share go to on the EVM chains; null when no chain uses that bucket.
tokenomics.solana{ account, owner, treasury, community }: the coin’s tokenomics account on Solana and its wallets there; null without Solana.
tokenomics.version1 for the config above. 2 for tokenomics v2: perChain is then each chain’s { burn, buyHold, liquidity, holders, treasury, wallets, rewards } (shares in basis points; wallets the payout wallets with theirs, rewards the holder rewards’ tokens, native for the chain’s own coin), with vault, account (Solana), owner, ownerSol and holders (who qualifies). Another value is a kind this API does not describe yet, and carries only version and vault.

GET /coin/{id}/supply

Supply, GEThttps://www.pangea.trade/api/coin/{id}/supply

The answer is { slug, name, symbol, launchId, launchChains, degen, supply, live, liveError? }. supply is the same as on the coin. For a degen mode coin, live is read from the chains and kept a minute: { mode, total, reserve, circulating, burned, burnedUnread, perChain, burnedPerChain, reserveAddress, decimals, onSolana, chains }, in base units as strings, where total is what the tokens report, reserve what Solana’s bridge reserve at reserveAddress holds now, and circulating the total less what was burned (never less the bridge reserve, which backs the other chains and is not extra supply); perChain is what is on each chain, Solana’s its own float with the bridge reserve apart. A figure the chains did not give is null. live is null for any other coin and with ?live=0; liveError: "busy" means the shared budget of chain reads is spent for the minute.

GET /coin/{id}/tokenomics

Tokenomics, GEThttps://www.pangea.trade/api/coin/{id}/tokenomics

The answer is { slug, name, symbol, launchId, launchChains, tokenomics, tokenomicsKnown, chains }. tokenomics is the coin’s config, as on the coin. chains is each chain’s vault read from the chain, { tokenomics }, or null with an error when that chain could not be read. A vault has its split, buckets (what waits in each, in the chain’s native unit), spent, and totals: receivedWei, coinBurned, coinToTreasury (bought and held), liquidityAdded, gasRepaidWei. A coin its launch’s config says has no tokenomics is read on no chain. ?live=0 answers the config alone. Each chain’s reading is shared with the coin page and kept a while: a vault five minutes, no vault 24 hours, a chain that could not be read half a minute. 404 while tokenomics is switched off on this deployment.

GET /v1/launches

Launches, GEThttps://www.pangea.trade/api/v1/launches?limit=25

Every coin launched here, newest first: { launches, next, latest, readAt }. Each launch is { id, slug, name, symbol, launchId, listedAt, launchedAt, launchChains, chains, coinId, v4, degen, factoryGen, supplyMode, factoryV3, supply, tokenomics, image, platform }: chains each chain’s { token, pool, quote } (quote as in GET /coin/{id}), factoryV3 true for a factory v3 coin, supply is { total, circulating, reserve, reserveChain }, tokenomics is { on: false }, or { on: true, version, vault, buckets, chains } naming the buckets any chain uses, or null while it is not known; image the coin’s logo on this site, or null until it is stored. For a v4 coin, coinId is its coin id and v4 is { coinId, home, listed, open, notOpen, transports, role: { creatorEvm, creatorSvm, renounced } }: its home chain, the chains it lists, which of them are open now, its transports and its creator role; each open chain carries openedAt, unix seconds. Both are null for any other coin. A coin is listed once it has landed and its pools are open. id is its cursor, <listedAt>:<slug>.

  • limit: 1 to 100, 25 by default.
  • before: the previous page’s next, for the page after it. next is null on the last page. To catch up, send the same since with each page.
  • since: a cursor, for the coins listed after it, or unix seconds, for the coins listed in that second or later. A coin can be listed a little after one with a later time, so to catch up, ask from a minute or two before the newest you have and drop the ids you already hold.
  • latest: the newest coin’s cursor. Open the stream with it as since and nothing is missed between the two.
The newest launches, then the page after
curl -s "https://www.pangea.trade/api/v1/launches?limit=10"
curl -s "https://www.pangea.trade/api/v1/launches?limit=10&before=<next>"

GET /v1/launches/stream

Launches stream, GEThttps://www.pangea.trade/api/v1/launches/stream

Every new launch as server-sent events, the moment it is live. Each is an event: launch with the launch, as /v1/launches lists it, as its data, and, for a launch newer than any sent before, its cursor as the event id. After the first catch-up comes one event: ready with { latest }. To miss nothing across a reconnect, send the last id you saw as Last-Event-ID (an EventSource does this by itself) or as ?since=: the launches listed after it come first, oldest first, up to 100 at a time. Without either, only launches from now on. The ready event carries the stream’s place as its id too, so a reconnect before any launch still resumes. A coin can become visible a little after one listed later, so the stream also looks two minutes back and sends such a coin with no event id, keeping your place. A resume sends those two minutes again, so a launch can come twice: drop the ones whose data’s id you have. When a v4 coin already sent opens another of its listed chains later, the stream sends an event: chain_opened for that same launch, never a new launch: its data is { key, coinId, chain, openTime, slug, id, launch }, keyed by coin id, chain and open time, with no event id. Clients dedupe by coinId: a key you already hold is a repeat. A resume sends again each chain opened after your cursor’s time. A comment line comes every 15 seconds; the stream closes after 30 minutes, so reconnect with the last id. Send Accept: text/event-stream. At most 3 open per caller; it takes requests from programs and from the Pangea site, like the Launch API.

Server-sent events are the simplest realtime API: one long GET that any HTTP client can read, that resumes by itself, and that passes every proxy. For pools, trades and bridges as they happen, open the live WebSocket below.
Follow new launches with curl
curl -N -H "Accept: text/event-stream" "https://www.pangea.trade/api/v1/launches/stream"
Follow new launches with EventSource (Node: npm install eventsource)
import { EventSource } from "eventsource";

const stream = new EventSource("https://www.pangea.trade/api/v1/launches/stream");
stream.addEventListener("launch", (e) => {
  const coin = JSON.parse(e.data);
  console.log(coin.symbol, coin.degen ? "degen" : "", coin.chains);
});
// It reconnects by itself and sends the last id as Last-Event-ID, so nothing is missed.

WebSocket /live

The socket the site’s own pages use, open to everyone and read-only: wss://www.pangea.trade/live. No key, any origin or none. You subscribe to topics and are pushed what changes: pool readings, trades, bridges and new coins. Everyone subscribed to a topic is sent the very same message, made once, so hold as many sockets as you need, up to 50 from one IP address.

It is a wake-up channel, not a second source of truth: every value on it is also the answer of a REST endpoint here. A client that misses something on the socket is right again after its next REST read, and the rules below say when one is needed.

TopicWhat it carries
poolsdiffLog: the pool reading as small patches, about once a second while anything trades. See the pool reading, as patches, below.
poolsState: the pool reading of GET /pools. It is larger than one message may be, so it arrives as reset with size: follow poolsdiff instead.
pools:<slug>State: one coin’s pools, the same shape with one launch in it, replaced each time.
launchesState: the coin list, { readAt, launches: [{ slug, name, symbol }] }. A new coin appears here.
trades, trades:<slug>Log: one event per trade, of every coin or of one. Cursor letter s.
bridges, bridges:<slug>Log: one event per bridge transfer. Cursor letter b.
healthState: whether the feeds behind the socket are up. While delayed is true, poll REST.
job:<launchId>State: a launch job’s status, as the launch status route answers it. It is pushed again on every step of every chain, as it happens: see A launch, step by step, below.
relay:<requestId>The site’s own pages only: asked for from anywhere else, the socket is closed with 1008.

A launch, step by step

  • Subscribe to job:<launchId> (the launch id in lowercase) from the moment the fee is paid. Each message is the whole status, so replace what you hold.
  • Per chain, d.result.sponsoredLegs while the launch runs (d.result.legs once it is finished) holds { state, step, at, ms?, tx?, why?, listed? }.
  • step is one of paid (the fee is confirmed and the chain is queued), sending, sent (tx is the open’s hash, or its signature on Solana), confirmed, open (the pool is live), failed (why says why) or handover (the creator opens that chain from their own wallet). Steps only move forward.
  • at is when the step happened, in milliseconds since the epoch. ms is how long the chain took. listed: true means that chain is in the coin list.
  • d.result.coinId and d.result.coinListedAt appear the moment the coin is listed, before the job is done. From then on GET /coin/[id] answers.
  • The chain the fee was paid on opens first. Every other chain then opens at the same time.

Messages

  • You send JSON text of at most 1024 bytes: {"op":"sub","topics":["trades","poolsdiff"],"cursor":"s:1200,p:1762150000000123"}, {"op":"unsub","topics":["trades"]} or {"op":"ping"}. At most 12 topics on one socket. Anything else closes the socket with 1008.
  • The server first sends {"t":"hello","v":1,"now":ms,"hbMs":15000,"maxTopics":12,"sources":{...}}.
  • A state topic sends {"t":"pools","topic":"pools:abc","d":{...}} (and likewise launches, health, job): replace what you hold. On subscribing you are sent the current value at once.
  • A log topic sends {"t":"trade","topic":"trades","seq":1201,"slug":"abc","d":{...}} (and likewise bridge, poolsdiff): apply it only if its seq is above your cursor for that stream, then move the cursor to it.
  • {"t":"ready","topics":[...],"cursor":"s:1250"}: the replay of those log topics is done and they are live; the cursor is where each stream now stands.
  • {"t":"reset","topics":[...],"why":"replay"}: you are not subscribed to those topics. Read them over REST, then subscribe again with the cursor that read gives. why is replay (more than 1000 rows behind), size, ahead, unavailable or busy (wait a few seconds first).
  • {"t":"hb","now":ms} comes every 15 seconds. If nothing at all arrives for 40 seconds, reconnect.
  • {"t":"bye","reason":"rotate","retryMs":2100} comes before the server closes the socket: rotate (every socket is closed after about 30 minutes), slow, shutdown or rate. Reconnect after retryMs.

Reconnecting without missing anything

  • Keep one cursor per stream: the seq of the last trade (s), bridge (b) and pool patch (p) you applied, and subscribe with them, as "cursor":"s:1200,b:77". You are sent every row after them, then ready, then live: no gap and no repeat. Without a cursor a log topic starts from now, and ready gives you the cursor to keep.
  • When the socket closes without a bye, wait a random time between zero and a ceiling that starts at half a second and doubles up to 30 seconds, reconnect, and subscribe again with your cursors.
  • Slow readers are cut off, not waited for. A socket holding 256 KiB unread is not written to; meanwhile states keep only their newest value, and past 500 queued events the socket is told bye with slow. Reconnect with your cursors and nothing is lost.
  • Messages you send are counted per socket, a burst of 60 and then 10 a second, pings included. Past that the socket is told bye with rate.
  • Compression (permessage-deflate) is offered, and most clients take it on their own. The messages are the same either way.

The pool reading, as patches

  • Read https://www.pangea.trade/live/pools: { gen, body }, where body is a pool reading in the shape of GET /pools. Then subscribe to poolsdiff with "cursor":"p:<gen>". It answers 60 reads a minute from one address: read it at the start and after a reset, not on a timer.
  • Each poolsdiff event’s d.base is the gen it applies to, and its seq is the gen it makes. Apply it only on top of exactly the gen you hold. When d.body is present it is a whole new reading.
  • Otherwise d is { base, top, set, order }. top holds the reading’s top-level fields. Each entry of set is a launch by slug: whole as v, or its changed fields as f and its changed pools as pools, matched by chain (each whole as v or as changed fields f). order, when present, is the new order of the slugs.
  • A patch on a gen you do not hold, a reset of poolsdiff, or a patch naming a launch or pool you do not have: read https://www.pangea.trade/live/pools again and subscribe with its gen.
Follow trades and pools over the live socket (Node 22 or later, or a browser)
const LIVE = "wss://www.pangea.trade/live";
const BASE = "https://www.pangea.trade/live/pools";

let pools = null; // { gen, body }
let tradeCursor = null;
let attempt = 0; // reconnects since the last ready
const waits = {}; // topic -> how long to wait before asking for it again

function applyPatch(body, d) {
  const bySlug = new Map(body.launches.map((l) => [l.slug, l]));
  for (const e of d.set) {
    if (e.v) {
      bySlug.set(e.slug, e.v);
      continue;
    }
    const old = bySlug.get(e.slug);
    if (!old) return null;
    const l = { ...old, ...(e.f ?? {}) };
    if (e.pools) {
      l.pools = old.pools.slice();
      for (const pe of e.pools) {
        const i = l.pools.findIndex((p) => p.chain === pe.chain);
        if (i === -1) return null;
        l.pools[i] = pe.v ?? { ...l.pools[i], ...(pe.f ?? {}) };
      }
    }
    bySlug.set(e.slug, l);
  }
  const order = d.order ?? body.launches.map((l) => l.slug);
  return { ...d.top, launches: order.map((slug) => bySlug.get(slug)) };
}

async function readBase() {
  const res = await fetch(BASE);
  if (!res.ok) throw new Error(`GET /live/pools answered ${res.status}`); // 429 or 503: try again later
  pools = await res.json();
}

// Ask for one topic again later, waiting longer each time (2 s, doubling to 60 s).
function later(ws, topic) {
  waits[topic] = Math.min(60_000, (waits[topic] ?? 1_000) * 2);
  setTimeout(async () => {
    if (ws.readyState !== WebSocket.OPEN) return;
    try {
      if (topic === "poolsdiff" && !pools) await readBase();
      subscribe(ws, [topic]);
    } catch {
      later(ws, topic);
    }
  }, waits[topic]);
}

function subscribe(ws, topics) {
  const cursor = [];
  if (topics.includes("trades") && tradeCursor !== null) cursor.push(`s:${tradeCursor}`);
  if (topics.includes("poolsdiff")) cursor.push(`p:${pools.gen}`);
  ws.send(JSON.stringify({ op: "sub", topics, cursor: cursor.join(",") }));
}

async function connect() {
  try {
    if (!pools) await readBase();
  } catch {
    setTimeout(connect, Math.random() * Math.min(30_000, 500 * 2 ** attempt++));
    return;
  }
  const ws = new WebSocket(LIVE);
  let heard = Date.now();
  let retryMs = null; // from bye
  const watch = setInterval(() => Date.now() - heard > 40_000 && ws.close(), 5_000);
  ws.onopen = () => subscribe(ws, ["trades", "poolsdiff", "launches"]);
  ws.onmessage = (e) => {
    heard = Date.now();
    const m = JSON.parse(e.data);
    if (m.t === "trade" && (tradeCursor === null || m.seq > tradeCursor)) {
      tradeCursor = m.seq;
      console.log("trade", m.slug, m.d.side, m.d.valueUsd);
    } else if (m.t === "poolsdiff" && pools && m.seq > pools.gen) {
      const body = m.d.body ?? (m.d.base === pools.gen ? applyPatch(pools.body, m.d) : null);
      if (body) pools = { gen: m.seq, body };
      else {
        pools = null; // behind: start again from the base, on the same socket
        later(ws, "poolsdiff");
      }
    } else if (m.t === "ready") {
      attempt = 0;
      for (const t of m.topics) delete waits[t];
      const s = /s:([0-9]+)/.exec(m.cursor ?? "");
      if (s && tradeCursor === null) tradeCursor = Number(s[1]);
    } else if (m.t === "reset") {
      // Not subscribed to these any more. Keep the socket; ask again later, waiting longer each time.
      for (const topic of m.topics) {
        if (m.why === "replay" || m.why === "ahead") {
          if (topic === "trades") tradeCursor = null; // too far behind: fill the gap from GET /history
          if (topic === "poolsdiff") pools = null;
        }
        later(ws, topic);
      }
    } else if (m.t === "bye") {
      retryMs = m.retryMs;
    }
  };
  ws.onclose = () => {
    clearInterval(watch);
    setTimeout(connect, retryMs ?? Math.random() * Math.min(30_000, 500 * 2 ** attempt++));
  };
}

connect();

GET /holders

Holders, GEThttps://www.pangea.trade/api/holders

Query: token, a slug, required; chain, optional, to answer for one chain. The answer is { slug, readAt, launchTotal, degen, supply, chains, all }, where degen and supply are as on /coin/[id], and each of chains is { chain, count, top10Pct, rows, complete, note?, error? } and each row { chain, address, balance, pct, label }, balances in whole tokens, at most 100 rows a chain. all combines the chains, { rows, count, partial, top10Pct }, and is left out when chain is given. label names a known holder such as the pool or the bridge escrow. An answer is reused for a minute. Errors: 400 without a token, 404 for an unknown launch or a chain it is not on.

GET /bridge/balance

Balance, GEThttps://www.pangea.trade/api/bridge/balance

Query: token (slug), chain and address, all required, and asset=native to read the chain’s native asset instead of the token. The answer is { slug, chain, address, balance, decimals, reason?, reserve? }: balance in base units as a string, or null with a reason when the chain could not be read, never a false zero. On Solana the token balance is the sum of the owner’s token accounts for the mint. For the native asset, reserve is the gas the app keeps back for a trade. Errors: 400 for a missing field or an address of the wrong kind, 404 for an unknown launch or chain.

POST /bridge/quote, in full

Beyond the fields in For integrators, the body may carry slippageBps for the fast lane, 50 unless given and 1000 at most, and smartWallet, true when the sender is a contract wallet, and gapToleranceBps, a factory v3 token's tolerance under the gap estimate for the minimum credited (100 unless given, 2000 at most). The answer’s routes holds two routes, canonical and fast, and a route that cannot be used says why in unavailable. Besides the fields listed there, a route may carry:

FieldWhat it is
labelStandard or Fast.
fastFee, relayerFee, relayerOn the fast route: the fast lane’s fee, the relayer’s price and who it is, in base units.
minReceivedOn the fast route: the least the order lets the recipient be paid.
approvalOn the fast route from an EVM chain: the approval to send first, { to, data, value, chainId }, or null when none is needed.
solanaFastOn the fast route out of Solana: { relayerFee, minReceived, nativeFeeCap }, for /bridge/solana-fast-tx (nativeFeeCap: the most the order signs for LayerZero, also the route's nativeFee).
fillDropFee, layerZeroFee, fillDropNoteA fast order into Solana: the Fill drop (the instant fill's account rent, paid once to Pangea's delivery) and LayerZero's part of nativeFee.
routePaymentFrom an EVM chain into a Solana route that is not open: the one-time setup payment, { to, data, value, chainId }, to send before the transfer.
setupNote, messageFeeLaterOut of Solana with a route to open: the setup is paid first and LayerZero’s fee is priced after it.
feeIsMaximumnativeFee is the most the Solana transaction will sign; LayerZero charges what its workers quote.
noteA sentence about the route, for showing to a person.
gapA factory v3 token's standard route: the price-gap estimate. state (estimate, free, held or unavailable), burned (what the destination withholds, in decimals), minCredited and toleranceBps (the least it may credit, and the tolerance it was set with), minCreditedSource (out of Solana, in Solana's decimals), cover (the gas cover, when it may apply), source (a Solana source's price record), delivery (into Solana: { amount, note }, our delivery key's cost withheld from the arrival, in decimals; its ata, when present, is the part that is a new wallet's token-account rent on its first arrival) and poke (an EVM source's price record to send first), and warning (when set: a sentence to show before the send -- e.g. an arrival too small to pay a new wallet's token-account rent, which waits on Solana until that account exists; web-r4-b I-5). What arrives is amountOut less burned and, into Solana, less delivery.amount.
keysOnlyDeliveryFee, keysOnlyDeliveryNoteA factory v3 send into Ethereum: the part of nativeFee that pays Pangea's delivery there (in the source's wei; LayerZero's executor does not deliver there). Paid once, inside the same transaction.
dstDeliveryFeeOut of Solana toward Ethereum (a factory v3 token): the part of nativeFee that pays Pangea's delivery there, in lamports.
deliveryFee, deliveryPayeeThe site's instant-delivery fee inside nativeFee (when on), and the payee the send's refund address names for it.
needsRecordA factory v3 send out of an EVM chain whose price log needs records (GapNeedRecords): { pokes, poke }. call is null and gap.poke carries the same { to, data, value, gas }: send it from the sender's wallet about pokes times, one mined before the next, then ask the quote again.
splitA factory v3 send above what one transfer carries now: { text, count, minutes, parts, maxSendLD, cosign, cosignTx?, cosigner?, perPartFee, totalFeeEstimate, perPartLines, partGap? }. call is null. Every part is its own transfer and pays the full per-send fees: perPartFee (the first part's quote, itemised in perPartLines) and totalFeeEstimate (perPartFee x count). From an EVM chain, cosignTx is the whole send, unsigned, for POST /api/cosign ({ chain: "evm", chainId, tx, gapToleranceBps? }): its answer is the co-signed send with its fee (nativeFee, tx.value) and the minimum it guarantees at the co-signed price (minCreditedLD); a 503 means send in parts. From Solana, cosign is true only when the co-sign service is set up and cosigner names the GapGlobal co-signer: POST /api/bridge/solana-tx with cosign: true (and minCredited) builds the unsigned co-signed send (stage "cosign"), POST /api/cosign ({ chain: "solana", tx, gapToleranceBps? }) co-signs it and answers minCreditedLD and expectedMinCreditedLD; the wallet signs last. Before it signs, compare the answer's message bytes with the ones you built: they must be identical, with your wallet's signature slot still empty and only the co-signer's filled (the proxy never sees what you built, so a client of POST /api/cosign makes this check itself, as the site's own panel does). Any 503, ABOVE_COSIGN_MAX or mismatch means send in parts.

Further errors: 409 when the token has not launched on the destination, 424 when a Solana recipient could not be checked, and 424 with retry: true when pricing took longer than 45 seconds.

POST /bridge/solana-tx

Out of Solana, POSThttps://www.pangea.trade/api/bridge/solana-tx
The body you POST
{
  "slug": "<launch slug>",
  "to": "base",
  "amount": "1000000000000",
  "sender": "<Solana wallet>",
  "recipient": "0x<EVM address>"
}

Builds the unsigned transaction that sends the token from Solana to an EVM chain, for the sender to sign. For a token with the price-gap fee, minCredited is required: the least the destination may credit after it, in Solana base units (the bridge quote’s gap.minCreditedSource); without it the request is refused (400). after and afterValidUntil are given only on a second call after a setup step. The answer carries the request’s from, to, amount and symbol and one of three stages:

StageWhat to do
sendSign transaction, a base64 versioned transaction, and send it. received is what arrives, nativeFee LayerZero’s fee and nativeFeeCap the most it will sign.
togetherThe same, with the route’s opening in the same transaction. setupFee is the opening’s part of totalFee.
setupThe opening alone. Sign and send it, then call again with after set to its signature for the send.
recordA factory v3 token whose Solana price log holds no recent price: sign transaction (record_price, its signature fee only), send it, then call again with recorded set to its signature. Until it has landed and a slot has passed the answer is 202 with { pending: true, stage: "recording" }; then it is the send.

On Solana a factory v3 arrival is delivered with 650,000 compute units. A charged v3 receive there runs only through LayerZero's executor (its own execute flow) or Pangea's delivery keys (R1′), so nobody can land it at a price of their choosing; an escaped, refunded or unpooled arrival stays open to anyone.

Every stage carries blockhash and lastValidBlockHeight: sign within about a minute. While a setup step is still settling the answer is 202 with { pending: true, stage: "waiting" }; ask again. Errors: 400 for a bad field or a signature that is not this transfer’s setup, 404 for an unknown launch, 409 when the setup ran out or failed, the route is unusable or the wallet is short of SOL, 424 when the build takes longer than usual, with retry: true, and 424 after 50 seconds.

POST /bridge/solana-fast-tx

Fast out of Solana, POSThttps://www.pangea.trade/api/bridge/solana-fast-tx

The fast lane out of Solana: one unsigned transaction holding Pangea’s fast-bridge order and the OFT send that carries it. Body: { slug, to, recipient, amount, relayerFee, minReceived, sender }, with relayerFee and minReceived from the quote’s solanaFast. The answer is { stage: "send", transaction, orderId, amount, arriving, fastFee, relayerFee, nativeFee, nativeFeeCap, blockhash, lastValidBlockHeight }. Errors: 409 when the fast lane is off for this launch or route, 422 when the order could not be built, nothing having been sent.

POST /bridge/route-open

Open a Solana route, POSThttps://www.pangea.trade/api/bridge/route-open

The second step of the first transfer from an EVM chain into a Solana route: once the quote’s routePayment has been sent, this hands its transaction to Pangea’s delivery service, which checks the payment on chain and opens the route, or refunds it. Body: { slug, from, txHash, sender }, from being the EVM chain the payment was made on. With Accept: text/event-stream it streams { state, reason?, refundTx? } as the route is opened; otherwise it answers once. state is one of verifying, paid, opening, open, refunded, refund-owed, review and refused. Send the transfer once it is open. Errors: 404 for a launch not on Solana, 424 when routes cannot be opened from this deployment.

POST /bridge/arrival

Arrival stream, POSThttps://www.pangea.trade/api/bridge/arrival

Body: { txHash }, the send’s transaction hash or Solana signature. A stream of { state, route, guid, dstTx?, by? }, state being in-flight and then delivered, with the delivery’s transaction in dstTx. It is fed by Pangea’s delivery service, which remembers a transfer for 30 minutes. Errors: 400 for a malformed hash, 424 when the deployment does not serve arrivals; LayerZero Scan answers the same question.

POST /bridge/refund

Held fast orders, POSThttps://www.pangea.trade/api/bridge/refund

Body: { chain, orderId }, the destination chain and the fast order’s id. The answer is { held: false } or { held: true, guid, amount, srcEid, call }, where call is the refund(guid) transaction, { to, data, value, chainId }, with LayerZero’s fee for the trip back as its value. It looks back 5,000 blocks. Errors: 400 for a malformed id or a chain without a fast bridge.

POST /bridge/orders

Report a fast order, POSThttps://www.pangea.trade/api/bridge/orders

Body: { from, hash }, the chain a fast order was posted on and its transaction. The board reads the order from the receipt and remembers whose price it was sent at, which is how a relayer that does not fill is benched. The answer is 202 with { recorded: false, pending: true } before the receipt exists, then { recorded, relayer, why?, orderId, to, srcBridge }. Only orders priced from this deployment’s board are recorded; why says why not.

POST /chain/tx

Transaction stream, POSThttps://www.pangea.trade/api/chain/tx

Body: { chain, hash }. One event when the transaction is in a block: { state: "mined", success, block } on an EVM chain, { state: "mined", success } on Solana, or { state: "unseen" } if it never appears, after up to 10 minutes on an EVM chain and 5 on Solana.

Trading: POST /trade/quote and POST /trade/solana-tx

These are the trade card’s own and may change; read a quote for a price, and build trades from the pools directly where you can. /trade/quote takes { slug, side, chain, amount, user?, solanaUser?, slippageBps?, mode?, intent?, quoteAsset? }: side is buy or sell; chain is the chain the wallet trades on, always a chain name (auto is refused, and a fromChain other than chain is refused too); amount is the pay asset’s base units for a buy, the token’s for a sell. slippageBps is the slippage, how far below the quote the trade may fill: a whole number of basis points from 1 to 9900 (0.01% to 99%), 100 unless given. mode is "direct" (the default: the coin’s own pool on that chain) or "cheapest" (also price the other chains’ pools and offer the route when it is better after every fee); intent is "preview" (a price, nothing to sign) or "execute"; quoteAsset: "stock" pays or receives the stock token itself on a stock-paired EVM chain. A direct trade goes through the coin’s own pool (details.router: "pool"), with Jupiter as a fallback on Solana. The answer is { mode, preview?, chain, fromChain, crossChain, cheapestNote, unroutable, details, steps, cheapest? }; with mode: "cheapest", cheapest is { offered, reason, plan, steps, comparison: { directOut, routeOut, gainBps, cost }, tried } and, when it is offered, directDetails is the direct trade beside it. Cheapest mode has its own brakes (20 a minute per wallet, 2 at once per caller); past them it answers as a direct quote with the reason in cheapestNote. Errors: 400, 404, 422 with the reason when no route was found, and 424 when a chain or the router could not be reached or took too long (with retry: true where trying again can help). Never a 5xx: a proxy in front may replace a 5xx body.

/trade/solana-tx takes { instructions, addressLookupTableAddresses?, payer }, a swap step’s Solana instructions as the quote gives them, at most 24 instructions and 8 lookup tables, and answers { transaction, blockhash, lastValidBlockHeight }: an unsigned versioned transaction. It does not inspect what the instructions do, so read them, or let your wallet simulate them, before you sign.

E.4

Launch API

E.4

A program can launch a token the way the launch page does: quote it, get the transaction that pays for it, hand the paid launch over, and follow it until every chain of the launch is live. The endpoints are under https://www.pangea.trade/api/v1/launch. Prepare, submit, status and the SOL payment are the launch page’s own steps, handed to the same code, with the same checks.

  • No API key needed, and no rate limit for normal use. Only a flood ceiling far above any real integration applies, per IP address. A partner key takes you out of it: see API keys and limits below.
  • Programs, and the Pangea site. A program sends no Origin header and is served. A browser on another website always sends one, and is refused with 403, so another site cannot spend the reads a quote makes on five chains through its visitors. The status read is a read and takes any origin. The event stream holds a connection open, so it takes the same rule as the rest and needs Accept: text/event-stream (406 without it); a page's no-cors or cross-site request is refused with 403, and each caller may hold one stream per launch.
  • The platform signs the snapshot, never the API. Every chain launches with one price snapshot, signed by Pangea’s worker after it has found your payment on chain. No endpoint here signs one or sends anything for you.
  • Amounts are base units as decimal strings: wei on an EVM chain, lamports on Solana, and nine decimals for the token.

The flow

  • POST /v1/launch/prepare quotes the launch and returns a ticket: the launch’s every parameter and its fee, signed by Pangea. Every later step takes it.
  • POST /v1/launch/authorization only when another wallet sends your own chain’s launch: the permission your creator wallet signs for it. Launch with your own wallet →
  • POST /v1/launch/pay returns the one transaction that pays the fee. You send it from the payer’s wallet; Pangea sends nothing for you. Pay within the quote’s 10 minutes.
  • POST /v1/launch/submit hands the launch over once the payment is mined, within 24 hours of the quote, with the payer’s signature (payerAuth) and any creator signatures the quote asked for. Pangea checks the payment on chain and queues the launch.
  • GET /v1/launch/status and GET /v1/launch/events follow it: the price snapshot signed, then each chain as it goes live. Paid on an EVM chain, that chain goes first, then Solana, then the other EVM chains; paid in SOL, Solana goes first.
EndpointWhat it is forLimit per caller
POST /v1/launch/prepareThe quote, and the ticket.120 in ten minutes per address; none with an API key.
POST /v1/launch/authorizationThe permission your creator wallet signs for another launching wallet.300 in ten minutes per address, with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/payThe transaction that pays the fee.300 in ten minutes per address, with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/submitHands the paid launch to Pangea’s launcher.300 in ten minutes per address, with authorization, pay, submit and kit together; none with an API key.
GET /v1/launch/statusWhere the launch stands.None.
GET /v1/launch/eventsThe same, as server-sent events.3 open at once per address, 100 per API key.
POST /v1/launch/kitWhat you need to make your own launch-time buys.300 in ten minutes per address, with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/solana-self-callYour Solana launch call, with selfSolana.40 in ten minutes per address (90 for the landed-signature hint).
POST /v1/launch/solana-cosignedNot available yet.None.

Every endpoint above also counts toward the flood ceiling: 1200 requests a minute from one IP address, all /v1 endpoints together.

API keys and limits

There is no rate limit for normal use and no key is needed. Three ceilings stop floods, per IP address and counted together on every server: 1200 requests a minute across every /v1 endpoint, and 120 quotes in ten minutes from POST /v1/launch/prepare, which reads five chains every time, and 300 in ten minutes of authorization, pay, submit and kit together, which read the chains on every call. Each event stream holds a connection open, so at most 3 are open at once per address.

An integration that needs more, or wants a guarantee it will never meet a ceiling, can ask for a partner key: write to us through the support chat on the site with your project’s name and the IP addresses you call from (optional). Send it as the X-Pangea-Key header on every request. With a key there is no ceiling, at least 100 event streams may be open at once, and it can be limited to your addresses. Keep it on your server: never put it in a page or an app.

HeaderWhat it says
X-Pangea-KeyYour partner key, if you have one (sent by you). Leave it out to call without one.
X-RateLimit-LimitThe ceiling that applies to this request, in requests per window, or unlimited.
X-RateLimit-RemainingRequests left in the window, or unlimited.
X-RateLimit-ResetSeconds until the window starts again (0 without a ceiling).
Retry-AfterOn a 429: seconds to wait.
X-Pangea-Key-Statusvalid when your key was accepted; unverified when it could not be checked just then and the request was served as one without a key.

Every answer carries the three X-RateLimit headers. A key that is not valid or has been revoked is refused with 401 (never served as if it were absent), and a key used from an address it is not allowed from with 403.

The whole flow, with fetch
const API = "https://www.pangea.trade/api/v1/launch";

const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));
const get = (path) => fetch(API + path).then(async (r) => ({ status: r.status, body: await r.json() }));

// 1. The quote, and the ticket: pay, submit and the launch kit all take it.
const prepared = await post("/prepare", {
  name: "Example",
  symbol: "EXMPL",
  metadataURI: "ipfs://<the CID of your metadata document>",
  feeBps: 100,
  creatorEvm: "0x<your EVM creator wallet>",
  creatorSol: "<your Solana creator wallet>",
  payChain: "base",
});
if (prepared.status !== 200) throw new Error(prepared.body.error);
const { ticket, launchId, payerProof } = prepared.body;
console.log(`fee: ${prepared.body.fee.payment.send} wei, about $${prepared.body.fee.feeUsd}`);

// 2. The transaction that pays it: send it from pay.body.from, the payer, and wait for it to be mined.
const pay = await post("/pay", { ticket });
if (pay.status !== 200) throw new Error(pay.body.error);
const { chainId, to, data, value } = pay.body.tx;
console.log({ chainId, to, data, value, sendFrom: pay.body.from, before: pay.body.validUntil });
const payer = pay.body.from;

// The payer signs that it pays for this ticket: personal_sign of payerProof.message by payerProof.signer.
const payerAuth = "0x<that signature>";

// 3. Once it is mined, hand the launch over. A 402 means the payment is not found yet: ask again.
// If prepared.body.creatorProofs lists a creator wallet, add its signature as creatorEvmAuth / creatorSolAuth.
const submitted = await post("/submit", { ticket, payer, payerAuth });
if (submitted.status !== 200) throw new Error(submitted.body.error);

// 4. Where it stands: pending, running, done or failed, and each chain as it goes live.
const status = await get(`/status?launchId=${launchId}`);
console.log(status.body.state, Object.keys(status.body.result?.live ?? {}));

Errors

An error is { "error": "..." } with a status, and the message says what to do. A status read or an event stream for an id the queue does not have is 404 with { "launchId": "0x...", "state": "unknown" } instead.

StatusWhat it means
400The request is wrong: a field missing or malformed, a ticket that does not verify or is over a day old.
402Submit only: the payment is not in order. Not mined yet, or short: the answer carries paid, quoted, collector and chain. Ask again once it is.
401An X-Pangea-Key that is not valid, or has been revoked.
403A browser on another website, or an API key used from an address it is not allowed from.
404A launch id the queue does not have.
409The request conflicts with where the launch is: the quote has run out, a creator wallet or fee receiver to confirm (with needsConfirm), a pair or option that cannot be had, a kit for a launch not submitted yet, a permission for a launch that needs none, a payment for a launch already paid and queued (do not pay twice: a second payment toward the same launch id is kept as fee), and the co-signed Solana launch, which is not built yet.
410A SOL payment asked for with too little of the quote’s life left for it to land. Prepare again.
424Something Pangea depends on could not be read or is not configured: a chain, the launch queue, the launcher’s configuration. Launches closed is a 424 too. Nothing was queued; try again.
429Over a ceiling, or too many event streams open. The message says how many seconds to wait, and Retry-After says it too.

A body that is too large is 413, one sent compressed 415, and one that does not arrive in time 408.

POST /v1/launch/prepare

Prepare, POSThttps://www.pangea.trade/api/v1/launch/prepare
The body you POST
{
  "name": "Example",
  "symbol": "EXMPL",
  "metadataURI": "ipfs://<the CID of your metadata document>",
  "feeBps": 100,
  "creatorEvm": "0x<your EVM creator wallet>",
  "creatorSol": "<your Solana creator wallet>",
  "payChain": "base"
}
FieldWhat it is
name, symbolThe token’s name and its ticker (no space): any length, not empty. Neither has to be unique.
metadataURIipfs:// and the content hash of your token’s metadata document, nothing else. It is part of every EVM token’s address. The launch page pins the document for you, but its upload endpoint answers the Pangea site only: a program pins its own.
feeBpsThe trading fee, 30 to 500 basis points. Set once, for good.
feeBpsByChainOptional, factory v2: a different trading fee per chain, as {"base": 30, "bsc": 250}, each 30 to 500. A chain it does not name takes feeBps. Each chain's fee is signed into its own leg and set once, for good. The quote then answers legFees, every chain's fee, in place of feeBps.
creatorEvm, creatorSolYour creator wallets, on the EVM chains and on Solana: the coin’s creator, and where your share of the trading fee is paid unless payeeEvm / payeeSol or tokenomics says otherwise. A contract, or a Solana account that is not a plain wallet, is refused with 409 and needsConfirm unless confirmFeeWallet says it is yours.
payChainethereum, base, robinhood, bsc or solana; robinhood when left out. You pay once, on this chain, in its native asset. It must be one of the launch’s chains.
chainsOptional: the chains the launch is on, any two to five of ethereum, base, robinhood, bsc and solana, at least one of them an EVM chain. All five when left out.
supplyMode0, the default and, on factory v3, the only one: 1B on each chain, all of it pooled. A v3 launch refuses 1 (factory v2’s 1B in all) with 409.
degenOptional: the launch form’s degen switch. On factory v3, true opens every chain at the live minimum market cap ($600 a chain today) when marketCapUsd is left out; the supply is 1B a chain either way. false, or leaving it out, is the standard opening.
marketCapUsdOptional: the opening market cap of the whole launch, every chain together, whole US dollars: 3000 to 500000 times the number of chains (rounded down to a whole dollar a chain), or 3000 to 500000 with degen; 3000 a chain when left out. Every chain opens at the one price this over the whole supply; the quote's v2.marketCapUsd is the total it opens at. On factory v3 (no supplyMode 1): 600 (the live minimum, 600 to start, at most 3000) to 500000 a chain times the chains; with degen true and no marketCapUsd, the minimum a chain times the chains; otherwise 3000 a chain when left out.
payeeEvm, payeeSolOptional: the fee receiver on the EVM chains and on Solana, when the trading fees should go somewhere other than your creator wallets. Empty is your creator wallet. An EVM receiver with code on one of the launch’s chains is refused with 409, needsConfirm: "payeeEvm" and contracts until confirmPayee lists it: a list of the receivers you confirm are your own contracts.
payerOptional: the wallet that pays the fee; your creator wallet on the pay chain when left out. Only that wallet’s own payment counts, and it signs for the launch before it is handed over (payerAuth at submit).
sameAddresstrue for one token address on every EVM chain of the launch, ending in 6ea.
tokenomicsV2Optional: custom tokenomics, the form the launch page sends (tokenomics v2.3). { minHolderBps, selectMode, selectCount, legs, confirmContracts? }: minHolderBps is the least a holder must hold to be paid, 1 to 10,000 basis points of supply (10 to 10,000 when an EVM chain's vault is on an earlier tokenomics factory); selectMode is "all", "top" or "random", with selectCount (0 for all, otherwise at least 1); legs names each chain of the launch, each { burnBps, buyHoldBps, liquidityBps, holdersBps, treasury?, wallets: [{ address, bps }], rewards?: [{ token, bps }], burnTargets?: [{ token, bps }], burnTargetsBps? }. A leg’s shares add up to exactly 10,000. Up to 10 payout wallets; treasury when buy and hold is used; rewards (1 to 5 tokens adding up to 10,000) when holders is used. A reward token is "native" (the pool’s pair asset: ETH or BNB, SOL, or the stock token on a stock-paired chain), "coin" (ETH or BNB, paid as WETH or WBNB, on a stock-paired EVM chain), or any token’s address (a mint on Solana). On a stock-paired chain the stock cannot also be named as a token reward or a burn target; a Solana leg paired with an asset pays holders in that asset only; Solana takes no burn targets. A wallet with code is refused with 409 and needsConfirm: "tokenomicsV2" until confirmContracts lists it. Not with tokenomics, payeeEvm, payeeSol, burnFees, selfLaunch or selfSolana. Custom tokenomics on v3 →
tokenomicsOptional, the older form, still accepted: spend your fee share automatically. { split, perChain?, treasury?, community?, treasurySol?, communitySol?, confirmContracts? }: split is five shares in basis points adding up to 10,000, in the order burn, buy and hold, add to liquidity, creator, community; perChain a split for each chain by name, naming only the launch’s chains (split covers any it leaves out). Nothing else may be in it. The wallets are needed when their share is used, the Solana ones on a launch with Solana. With it, leave payeeEvm, payeeSol, burnFees and selfLaunch out: the coin’s vault is the fee receiver. Coin tokenomics →
devBuyNot available at the moment: any amount but 0 is refused with 409 (“Dev buy temporarily unavailable”) at prepare, pay and submit. GET /api/launch/dev-buy says whether it is offered. To buy at launch, use buys.
buys, buysCapOptional: launch buys, first buys made inside the launch transaction on an EVM chain. buys is a list of { eid, recipient, amount }: the chain by its LayerZero id (30101 Ethereum, 30184 Base, 30102 BSC, 30416 Robinhood Chain), the wallet that gets the coin, and what it spends, in the smallest unit of the pool’s pair asset (wei, or the stock token’s units). Up to 10 on one chain and 32 in all, each chain within its gas limit; they run in the order listed. buysCap is required with them: the most they may spend, as a string. Their gas is in the launch fee. The money is deposited beforehand by creatorEvm into that chain’s launch-buys contract (deposit(launchId) with the native coin, or approve and depositToken for a stock token), and what is not spent is taken back with reclaim(launchId, asset). The answer’s launchBuys is { launchId, payer, buysCap, perChain: { <chain>: { contract, factory, eid, entries, total, asset, decimals } } }. Launch buys →
pairs, pairSolOptional: what a chain’s pool pairs against when not its native coin. pairs is { "<chain>": "<asset address>" } for the EVM chains, an asset that chain’s pair registry lists, inside its price band (empty or the zero address is the native coin). pairSol is a Solana mint on the splitter’s allowlist (empty or wrapped SOL is SOL). The stock tokens each chain offers: GET /api/launch/stocks.
stockAckWith a stock pair: { "<chain>": "<stock token address>" } for every chain prepare warns about. Until each warned chain is named, prepare answers 409 with needsAck: "stocks" and the warnings. It is signed into the ticket.
feesInNativetrue or false: “Receive fees in native” for the chains paired with a token (any pair but the chain’s own coin), true when left out. Stored with the launch, not signed: the creator can switch it later on their profile. Stock fees →
openRoutestrue to pay, in the same payment, for Solana’s four bridge routes to be opened right after the launch. The launch must include Solana; not with selfSolana. Its price is a line of the quote, fee.openRoutes, and GET /api/launch/open-routes gives it beforehand.
launchHookOptional: the fee hook the launch names, one the factories allow, the same address on every EVM chain. Each factory’s own when left out.
requoteThe ticket of a launch already paid whose quote is past its 24 hours: a new quote for the same launch id, pay chain and payer. The answer carries requote.
selfLaunchtrue to send your pay chain’s launch yourself. EVM pay chains only; for Solana’s launch, see selfSolana. Launch with your own wallet →
selfSolanaOptional: send Solana’s launch from your own Solana wallet, creatorSol, on any pay chain. Only where the deployment offers it; elsewhere prepare refuses it with 409. { nonce, launchId, oftStore, mint, mintNonce }, made in your browser from your Solana wallet’s signature of the seed message; the seed itself is never sent. Not with openRoutes, and payeeSol must be empty or the burn address. Launch with your own wallet →
selfLaunchFromWith selfLaunch: the wallet that sends it, when it is not creatorEvm. It then needs your creator wallet’s permission.
confirmFeeWallet{ evm: true } or { sol: true }: yes, that creator wallet is mine.
burnFeestrue to burn your creator fees: the fee receiver becomes 0x000000000000000000000000000000000000dEaD on the EVM chains and 1nc1nerator11111111111111111111111111111111 on Solana, for good. Leave payeeEvm and payeeSol empty with it. Your creator wallets stay yours, with the coin’s page and socials.
launchTotalFixed at 1B times the number of chains; anything else is refused.
moon, poolPercent, poolBps, salt, saltsRefused with 400: moon mode is removed, and the token addresses’ salts are Pangea’s to pick. Any other field prepare does not know is ignored.

The answer carries launchId, ticket, creator, fee (feeUsd, costUsd, ttlSeconds and payment: chain, asset, wei, devBuy, send, what the wallet sends in all, collector and, on an EVM chain, tx), prediction.perChain, each EVM chain’s token address, every one ending in 6ea, selfLaunch when asked for, and remaining, the steps left in plain sentences.

It also carries factoryGen, 2 for a factory v2 launch, degen, and with it v2: v2.degen, the supply (circulating, bridge reserve and total), what each pool holds at the open, the fee receivers, and legOrder, the order the chains are sent in and by whom. payer is the wallet whose payment counts. payerProof is { kind, signer, message }: the text the payer signs, personal_sign on an EVM chain or signMessage on Solana, posted to submit as payerAuth. creatorProofs lists the same for each creator wallet that is not the payer’s signing wallet, each { side, kind, signer, message }, posted as creatorEvmAuth and creatorSolAuth; it is empty when the payer is your creator wallet on every side that needs one.

POST /v1/launch/authorization

Authorization, POSThttps://www.pangea.trade/api/v1/launch/authorization

Body: { ticket }, for a launch quoted with selfLaunch and a selfLaunchFrom other than the creator wallet. The answer is { launchId, chain, signer, launcher, typedData, digest, next }: typedData is what signer, your creator wallet, signs with eth_signTypedData_v4, and digest the hash the factory recovers it from, for a signer that signs hashes. It is the same typed data prepare returned as selfLaunch.authorization. Post the signature to submit as creatorAuth. 409 for a launch that needs no permission.

POST /v1/launch/pay

Pay, POSThttps://www.pangea.trade/api/v1/launch/pay

Body: { ticket, payer }. Every amount comes from the ticket, never from the request. On an EVM chain, payer is not needed and the answer is { launchId, chain, tx, fee, devBuy, from, validUntil, next }: tx is { chainId, to, data, value }, a call of pay(launchId), or payWithDevBuy(launchId, devBuy), on that chain’s LaunchFeeCollector, with the fee and the dev buy as its value. Send it from from, the quote’s payer: only that wallet’s own payment counts toward the launch. validUntil is the end of the quote, in unix seconds. A dev buy on a chain paired with an asset is refused with 409: send the quote’s own fee.payment.tx instead.

Paid in SOL, payer is the paying wallet in base58, and the answer is an unsigned versioned transaction naming it as the only signer: { launchId, programId, lamports, feeLamports, devBuyLamports, payer, transaction, feeAccount, blockhash, lastValidBlockHeight, rentLamports, quoteAccountRentLamports }, transaction in base64. The payment’s own account needs rentLamports on top. Sign and send it within about a minute: it is good for as long as its blockhash. The paying wallet must be the quote’s payer.

Pay the launch fee in SOL with @solana/web3.js, then submit
import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js";

const API = "https://www.pangea.trade/api/v1/launch";
const ticket = process.env.LAUNCH_TICKET; // from prepare, with payChain solana
const payer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.SOLANA_KEYPAIR)));
const connection = new Connection(process.env.SOLANA_RPC, "confirmed");
const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));

// The unsigned payment, naming your wallet as its only signer.
const pay = await post("/pay", { ticket, payer: payer.publicKey.toBase58() });
if (pay.status !== 200) throw new Error(pay.body.error);
const tx = VersionedTransaction.deserialize(Buffer.from(pay.body.transaction, "base64"));
console.log(`paying ${pay.body.lamports} lamports, and ${pay.body.rentLamports} of rent`);

tx.sign([payer]);
const signature = await connection.sendRawTransaction(tx.serialize());
const confirmed = await connection.confirmTransaction(
  { signature, blockhash: pay.body.blockhash, lastValidBlockHeight: pay.body.lastValidBlockHeight },
  "confirmed",
);
if (confirmed.value.err) throw new Error("the payment failed");

// The payer signs prepare's payerProof.message (signMessage, ed25519): base58 or 0x-hex.
const payerAuth = process.env.PAYER_AUTH;

// A fee paid in SOL is found by who paid it: name the payer.
const submitted = await post("/submit", { ticket, payer: payer.publicKey.toBase58(), payerAuth });
if (submitted.status !== 200) throw new Error(submitted.body.error);
console.log(submitted.body.state);

POST /v1/launch/submit

Submit, POSThttps://www.pangea.trade/api/v1/launch/submit

Body: { ticket, payer, payerAuth, creatorEvmAuth, creatorSolAuth, creatorAuth }, once the payment is mined. payer is required for a fee paid in SOL, because that payment is found by who paid it; on an EVM chain it is optional and only recorded. payerAuth is required: the payer’s signature over prepare’s payerProof.message. creatorEvmAuth and creatorSolAuth are each listed creator wallet’s signature over its message in prepare’s creatorProofs. Without them nothing is queued, and submit answers with the messages to sign. creatorAuth is needed when another wallet sends your own chain’s launch. Submit never refuses a paid launch over it: without one that recovers to your creator wallet for that wallet, the launch is queued all the same, the answer carries selfLaunch: "dropped" and a warning, and Pangea sends that chain like the others. So check it first: POST /v1/launch/authorization with { ticket, creatorAuth } answers check: { ok }. The fee is read on chain, never taken from the request. The answer is { launchId, state, queued: true, enqueuedAt, creator, paid, next, selfLaunch?, warning? } with state: "pending". Submitting again queues nothing and answers { launchId, state, queued: false, message, result }.

GET /v1/launch/status

Status, GEThttps://www.pangea.trade/api/v1/launch/status?launchId=0x...

The answer is { launchId, state, message, lastError, attempts, enqueuedAt, result, selfChain, degen, tokenomics }, each field only when it is set. degen is whether the launch is in degen mode, and tokenomics whether it has tokenomics; the coin’s config is on /coin/[id] once it is live. state is pending, running, done or failed. In result: signed.at when the price snapshot is signed, live, each chain as it goes live, { token, at, tx }, and selfLaunch, your own launch call, for a launch that has one. selfChain names the chain you send yourself. signed.at and each chain’s at are ISO 8601 times; the call’s takeoverAt is in unix seconds.

GET /v1/launch/events

Events, GEThttps://www.pangea.trade/api/v1/launch/events?launchId=0x...

The status as it changes, as server-sent events: each an event: <name> line and a data: <json> line. Everything so far is sent the moment the stream opens, so a late listener misses nothing. A comment line, starting with a colon, comes every 15 seconds to keep the connection open. The stream closes after end, after 30 minutes, or when you disconnect; open it again to keep following. An id the queue does not have is 404: open the stream after submit has queued the launch.

EventDataWhen
state{ state }First, and whenever the state changes.
signed{ at }The price snapshot is signed.
pool-live{ chain, token, at, tx }That chain’s launch has landed and its pool is open. tx only when known.
self-launch{ chain, call }Your own launch call is out, once the price snapshot is signed. call is the same as the status’s result.selfLaunch.
self-launched{ chain, by }by is creator or platform: who sent your chain’s launch.
end{ state }done or failed. The stream closes.

At most 3 open streams per caller, and a few hundred in all on each server; past that it is 429 with Retry-After, and polling the status read does the same job. A page on the Pangea site can open it with an EventSource; a program reads it with a streaming fetch.

Read the event stream with fetch
const API = "https://www.pangea.trade/api/v1/launch";

// One frame: its event name and its JSON. Comment lines, such as the keep-alive, are skipped.
function parseFrame(frame) {
  let event = "message";
  const data = [];
  for (const line of frame.split("\n")) {
    if (line.startsWith(":")) continue;
    if (line.startsWith("event: ")) event = line.slice(7);
    else if (line.startsWith("data: ")) data.push(line.slice(6));
  }
  return data.length ? { event, data: JSON.parse(data.join("\n")) } : null;
}

async function* launchEvents(launchId) {
  const res = await fetch(`${API}/events?launchId=${launchId}`, { headers: { Accept: "text/event-stream" } });
  if (!res.ok) throw new Error(`the stream answered ${res.status}`);
  const reader = res.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  for (;;) {
    const { value, done } = await reader.read();
    if (done) return;
    buffer += decoder.decode(value, { stream: true });
    let end;
    while ((end = buffer.indexOf("\n\n")) >= 0) {
      const frame = parseFrame(buffer.slice(0, end));
      buffer = buffer.slice(end + 2);
      if (frame) yield frame;
    }
  }
}

for await (const { event, data } of launchEvents("0x<your launch id>")) {
  if (event === "pool-live") console.log(`${data.chain} is open: token ${data.token}`);
  if (event === "self-launch") console.log(`your launch call for ${data.chain} is out`, data.call);
  if (event === "self-launched") console.log(`${data.chain} was sent by the ${data.by}`);
  if (event === "end") break;
}

POST /v1/launch/kit

Launch kit, POSThttps://www.pangea.trade/api/v1/launch/kit

Body: { ticket, buys, deadline }, once the launch is submitted; before, it is 409. Each EVM chain’s token address and pool, ready calldata for your own buys, your launch call if you send one, and Solana’s pool once Solana has launched. The fields are under Launch-time buys →

POST /v1/launch/solana-self-call

Solana self-launch call, POSThttps://www.pangea.trade/api/v1/launch/solana-self-call

For a launch prepared with selfSolana. Body: { launchId, ticket }, the ticket the launch is queued under. Answers { launchId, call, nonce, blockhash, lastValidBlockHeight }: call is the template of your launch_self transaction (format pangea-solana-self-call/1), every byte that does not depend on your seed; nonce is the nonce your Solana wallet signs the seed message for again. 409 until it is out (once the price snapshot is signed and, paid on an EVM chain, that chain has launched), and once Solana has landed; 202 with retryAfterS while a fresh call is signed. With { launchId, signature } instead, you tell it the transaction your launch went out under: { kept }, or 202 while it has not landed.

POST /v1/launch/solana-cosigned

Not available. It always answers 409, with enabled: false and the reason: a Solana launch transaction signed by Pangea’s launcher, that you could submit yourself through Jito with your own buys after it, needs a change to the splitter program that is still in progress.

E.5

Launch with your own wallet

E.5

A launch is on its chains: all five, Solana, Ethereum, Base, BSC and Robinhood Chain, unless it was launched on selected chains. Paid on an EVM chain, that chain launches first, where the fee is checked; then Solana, from Pangea’s launcher, whose addresses derive from a secret seed that stays unpublished until it lands, or from your own Solana wallet if you choose that (below); then the other EVM chains. Paid in SOL, Solana launches first and the EVM chains follow, the EVM chains always sent by Pangea.

With selfLaunch: true, you send one EVM chain’s launch yourself: the chain you pay on, and only that one. Pangea’s launchers send the others, as for every launch.

Why nobody can launch your chain before you

Each EVM factory has two ways in. launch(LaunchParams p, LaunchTerms terms, Snapshot snap, bytes signature) takes three senders only: your creator wallet (msg.sender == p.creator), the factory’s admin, and a launcher the admin has listed. Anyone else reverts with NotLauncher. A creator may send only the pay chain’s launch; any other chain reverts with NotThePayChain.

launchAuthorized(LaunchParams p, LaunchTerms terms, Snapshot snap, bytes signature, bytes creatorAuth) takes any sender that holds your creator wallet’s permission naming that sender. The permission is EIP-712 typed data, LaunchAuth(bytes32 launchId, address launcher), in the domain { name: "PangeaLaunchAuth", version: "2" }, with no chain id and no contract. The factory recovers it against the sender, so a permission for one wallet is useless to anyone else who sees it; a signature that recovers to anyone but your creator wallet reverts with BadCreatorAuth.

So nobody else can send your chain’s launch, and it cannot be front-run. The price snapshot it carries exists only after your fee is paid: Pangea’s worker signs it after checking your payment on chain, and the API never signs one.

A creator wallet that is a contract, such as a Safe or a smart account, gives its permission through EIP-1271: the factory hands the same digest to its isValidSignature, and anything but the magic value reverts with ContractCreatorRefused. An EIP-7702 delegated wallet counts as one with code: its delegate’s isValidSignature decides and its plain signature is never recovered, so a delegate without EIP-1271 cannot give the permission. A launch with tokenomics cannot be sent from your own wallet.

The fee

The same as for any launch, quoted by prepare and paid once. With selfLaunch, the pay chain must be an EVM chain, and it is the chain you launch. That chain’s launch reverts with LaunchUnpaid unless the quote’s payer has paid the fee for it, and your launch transaction releases the fee to the platform in the same transaction (LaunchFeeCollector.onLaunch). Sending your chain yourself does not change the fee.

Your time to send it

  • Your call is handed out once the price snapshot is signed. Your chain goes first: Pangea sends Solana and the other EVM chains only once it has landed.
  • The option is on only where the deployment turns it on; elsewhere prepare refuses selfLaunch. Your time is counted from when your call is handed out: 90 seconds unless the deployment sets SELF_LAUNCH_GRACE_S to another, held between 90 and 120. It always ends at least 300 seconds before the snapshot’s deadline, and if the launch is retried it never ends later than the first time it was given. Its end is takeoverAt in your call, in unix seconds.
  • If your launch is not there by then, Pangea’s launcher sends it, with the same snapshot and the same parameters. Whichever of the two lands second reverts with AlreadyLaunched, and its sender loses only that transaction’s gas.
  • If your chain’s launch cannot land, nothing else is sent: no other chain launches before the pay chain.
  • The launch page stops offering the send 30 seconds before the takeover, since a transaction sent later may land after Pangea’s.

Step by step

  • POST /v1/launch/prepare with selfLaunch: true, payChain set to your chain, creatorEvm set to your creator wallet and, if another wallet will send the launch, selfLaunchFrom set to it. The answer’s selfLaunch is { chain, chainId, from, creator, factory, token, gasLimit, value, graceSeconds, authorization }.
  • If authorization is set, sign it with the creator wallet (eth_signTypedData_v4). You can also get it from POST /v1/launch/authorization with { ticket }, which answers { launchId, chain, signer, launcher, typedData, digest }. Sign before you pay. The typed data names its own domain type (EIP712Domain: [{ name: "name", type: "string" }, { name: "version", type: "string" }] in its types), so pass it whole: through eth_signTypedData_v4 or viem’s signTypedData, a wallet hashes the domain the contract checks.
  • POST /v1/launch/pay with { ticket } answers { tx: { chainId, to, data, value }, from, validUntil }. Send tx on your chain; with a dev buy, from from.
  • POST /v1/launch/submit with { ticket, payer, payerAuth, creatorAuth } once it is mined, with the payer’s signature as for any launch. creatorAuth is the permission, required when the launching wallet is not the creator wallet.
  • Watch GET /v1/launch/events?launchId= or poll GET /v1/launch/status?launchId=. Once the price snapshot is signed, the status’s result.selfLaunch and the self-launch event carry your call: { chain, chainId, launchId, from, to, data, value, gas, token, deadline, takeoverAt }, where to is the factory and data the encoded launch, or launchAuthorized with your permission, carrying the signed snapshot.
  • Send exactly that transaction, from from, before takeoverAt. Then result.selfLaunch.by says creator or platform; the status’s top-level selfChain names your chain throughout.
Sign the creator wallet’s permission for another wallet, with viem
import { privateKeyToAccount } from "viem/accounts";

const API = "https://www.pangea.trade/api/v1/launch";
const ticket = process.env.LAUNCH_TICKET; // from prepare, with selfLaunch and selfLaunchFrom
// The CREATOR WALLET signs: the creatorEvm you named. It lets the launching wallet send this launch.
const creatorWallet = privateKeyToAccount(process.env.CREATOR_WALLET_KEY);
const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));

const auth = await post("/authorization", { ticket });
if (auth.status !== 200) throw new Error(auth.body.error);
const { signer, launcher, typedData } = auth.body;
if (signer.toLowerCase() !== creatorWallet.address.toLowerCase()) throw new Error(`sign with ${signer}`);

// One signature for every EVM chain: the domain has no chain id and no contract.
const creatorAuth = await creatorWallet.signTypedData(typedData);

// Through eth_signTypedData_v4 yourself, pass it whole: it already names its domain's type (EIP712Domain).
const forWallet = typedData;
console.log("eth_signTypedData_v4 params:", [signer, JSON.stringify(forWallet)]);
console.log(`${launcher} may now launch ${auth.body.launchId}, and nobody else may use this`);

// Pay as usual, then hand it over with the payer's signature and the permission.
const submitted = await post("/submit", { ticket, payer: "0x<the wallet that paid>", payerAuth: "0x<its signature>", creatorAuth });
if (submitted.status !== 200) throw new Error(submitted.body.error);
Send your own chain’s launch, with viem
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base, bsc, mainnet, robinhood } from "viem/chains";

const API = "https://www.pangea.trade/api/v1/launch";
const CHAINS = { 1: mainnet, 8453: base, 56: bsc, 4663: robinhood };
const launchId = "0x<your launch id>";

// The wallet that launches: your creator wallet, or the selfLaunchFrom you named.
const account = privateKeyToAccount(process.env.LAUNCHER_KEY);

const status = await fetch(`${API}/status?launchId=${launchId}`).then((r) => r.json());
const call = status.result?.selfLaunch;
if (!call?.data) throw new Error("not out yet: it is handed out once the price snapshot is signed");
if (call.by) throw new Error(`already sent, by the ${call.by}`);
if (Date.now() / 1000 >= call.takeoverAt) throw new Error("too late: Pangea is sending it, with the same snapshot");
if (call.from.toLowerCase() !== account.address.toLowerCase()) throw new Error(`send it from ${call.from}`);

const chain = CHAINS[call.chainId];
const transport = http(process.env.RPC_URL);
const { maxFeePerGas, maxPriorityFeePerGas } = await createPublicClient({ chain, transport }).estimateFeesPerGas();
// What the wallet must hold: the whole gas limit at the max fee, and the value. Unused gas is refunded.
const mustHold = BigInt(call.gas) * maxFeePerGas + BigInt(call.value);
console.log(`hold at least ${mustHold} wei on ${chain.name}`);

const wallet = createWalletClient({ account, chain, transport });
const hash = await wallet.sendTransaction({
  to: call.to, // the factory
  data: call.data, // launch, or launchAuthorized with the permission, and the signed snapshot
  value: BigInt(call.value), // 1,000,000 wei: the opening buy
  gas: BigInt(call.gas),
  maxFeePerGas,
  maxPriorityFeePerGas,
});
console.log(`sent ${hash}; the event stream says self-launched when it lands`);

The examples sign with a private key to stay short. Any wallet that can sign typed data, and send a transaction with this data, value and gas limit, will do.

ChainChain idFactoryFee collectorUniversal Router
Ethereum10xf7052a4E38e241bc66bfAe6eB27D10B2A42cacB40x79d9a01B6ADB4938761e7D4fb29a802ea709F0Ec0x23617e59A5925b2A4Bf75d73ff6711cD0b29De85
Base84530xb405b396b607C3482A3d301b1cFF57A7de75325a0x7cb08218648433bbC9671f885E15954756717BCa0xd6145b2D3F379919E8CdEda7B97e37c4b2Ca9c40
BSC560x92858095Cadf9f9d60dC17226f7FF15Be4b69e450xcDB037B5B4b58e3C89d632463656D889123a7c040xDc264714F68d84CF29BC605589405E78bDBE7C9f
Robinhood Chain46630x9Cf5832474CBB09D2E06FF20460aff4303fAf72A0xcDB037B5B4b58e3C89d632463656D889123a7c040x204FAca1764B154221e35c0d20aBb3c525710498
Solana splitter programHb5uTppRbNxc3RjVRqTWqfdU3E3GiZyGYKccxYf9tUej

Gas

Your call comes with a gas limit of 11,797,500: a launch’s measured 8,250,000 gas, with 10% and then 30% of room. A launch uses up to about 8.25 million gas and the rest is refunded, but to send it your wallet has to hold the whole gas limit at its max fee per gas, plus the value.

Factory v3, every launch since 2026-11-02: the call goes to that chain’s factory v3 (Factories →), is handed to you once Solana’s launch has landed, and comes with prepare’s own selfLaunch.gasLimit: 14,300,000 (a v3 launch’s 10,000,000 with the same 10% and 30% of room), more with launch buys on that chain, at most the chain’s cap. Fund from that figure, or from call.gas.

The value is 1,000,000 wei: the opening buy, the pool’s first trade, made in the launch only when the launch has no dev buy. Send it as given: zero would skip the opening buy (and the token page could not tell the launch transaction's buys apart), and any other value reverts.

  • What your wallet must hold. At least 11,797,500 × max fee per gas + 1,000,000 wei, in ETH on Ethereum, Base and Robinhood Chain, in BNB on BSC. On Base, add its L1 data fee, which is charged on top of gas × price: a few cents is ample. The figure above is a floor: the call's gas is the node's estimate from your wallet with a 30% margin, never less than it, so fund from call.gas once the call is out.
  • What it costs you. About 8,250,000 × the gas price paid, plus the value. At 1 gwei that is 0.00825 of the chain’s coin. Read the chain’s gas price before you send.

Solana: launch it from your own Solana wallet

You can also send Solana’s launch yourself, from your Solana creator wallet, on any pay chain. Its Solana addresses then come from a seed only your wallet can make, so Pangea never holds it and nobody else can send Solana’s launch. It is a separate choice from selfLaunch: you can tick either, or both when you pay on an EVM chain.

  • Choosing it. On the launch page, with Solana among the launch’s chains, turn on Launch Solana from my own wallet. Your Solana wallet signs one message, the seed message; nothing is sent. Your browser makes the seed from that signature, finds a mint address ending in 6ea, and sends prepare only what the quote needs (selfSolana); the seed never leaves your browser.
  • The order. The pay chain goes first, because your fee is checked there. Paid on an EVM chain, that chain launches (from your own EVM wallet too, with selfLaunch); then Solana, from your Solana wallet; then Pangea’s launchers send the other EVM chains. Paid in SOL, Solana goes first from your wallet, and Pangea sends every EVM chain once it has landed.
  • Sending it. Once the price snapshot is signed, the launch page shows Your launch on Solana with a Send your Solana launch button. Connect the Solana creator wallet the quote named; no other wallet can send it. The wallet signs twice: the seed message again, so the page can rebuild the seed, and then one transaction, launch_self, which it sends. It is a v0 transaction through a shared lookup table, so your wallet must support those.
  • Your time. The price the launch carries is good only for a while, counted down on the panel. With less than 60 seconds left the button stops offering the send and a fresh price is signed for you, so keep the page open and send it when the button is back. You have 24 hours from when your first call is handed out.
  • If you don’t send it. Unlike your EVM chain, there is no takeover: Pangea’s launcher cannot send Solana’s launch for you, because it does not have the seed. Until you send it, nothing after Solana launches. After 24 hours no fresh price is signed and the launch stops there: paid in SOL, nothing has launched; paid on an EVM chain, that chain is already live and its fee settled, and the other chains wait for Pangea to look at it. Contact support.
  • What it costs you. Your Solana wallet pays the launch’s rent, about 0.028 SOL (Pangea’s fee no longer includes it), and the transaction’s fee, priority fee included. Hold a little more SOL than that before you send.
  • Its fees. A Solana launch you send yourself pays its trading fees to your Solana creator wallet, or burns them: another Solana fee receiver is refused. Its four Solana bridge routes are opened by their first bridgers, so it cannot be combined with paying for them to be opened at launch.

From a script: prepare with selfSolana, pay and submit as usual, then ask POST /v1/launch/solana-self-call with { launchId, ticket } until it hands over your call, build launch_self from it with your seed, and sign and send it from your Solana creator wallet. The helper the example imports, pangea-solana-self-launch.mjs, is the launch page’s own code for it in one file, safe in a browser and in Node. Download it beside your script and install what it imports: npm i @solana/web3.js viem tweetnacl.

Launch Solana from your own Solana wallet, from a script
import { Connection, Keypair } from "@solana/web3.js";
import nacl from "tweetnacl";
// Pangea's script helper, one file: download https://www.pangea.trade/sdk/pangea-solana-self-launch.mjs beside this script.
// It imports these, so install them: npm i @solana/web3.js viem tweetnacl
import { selfSolanaPrepareFields, seedMessageBytes, selfSolanaLaunchTx } from "./pangea-solana-self-launch.mjs";

const SITE = "https://www.pangea.trade";
const API = SITE + "/api/v1/launch";
// Your Solana creator wallet: it signs the seed message, and it sends and pays for Solana's launch.
const creator = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.SOLANA_KEYPAIR)));
const sign = (message) => nacl.sign.detached(message, creator.secretKey);
const connection = new Connection(process.env.SOLANA_RPC, "confirmed");
const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// 0. Offered here? And the program ids your launch's Solana addresses derive under.
const gen = await fetch(SITE + "/api/launch/factory-gen").then((r) => r.json());
if (!gen.selfSolana) throw new Error("this deployment does not offer the Solana self-launch");

// 1. selfSolana: sign the seed message for a fresh nonce and grind a mint ending in 6ea (a few seconds).
// Only the launch id, the OFT store and the mint go to prepare: the seed never leaves this script.
const selfSolana = await selfSolanaPrepareFields({ sign, programs: gen.solanaPrograms });
const prepared = await post("/prepare", {
  name: "Example",
  symbol: "EXMPL",
  metadataURI: "ipfs://<the CID of your metadata document>",
  feeBps: 100,
  creatorEvm: "0x<your EVM creator wallet>",
  creatorSol: creator.publicKey.toBase58(),
  payChain: "base",
  selfSolana,
});
if (prepared.status !== 200) throw new Error(prepared.body.error);
const { ticket, launchId } = prepared.body;

// 2. Pay and submit, as in the whole flow above (or in SOL, with payChain solana).

// 3. Wait for Solana's turn: the call is out once the price snapshot is signed (the signed event) and,
// paid on an EVM chain, that chain has launched. 409 "not ready to send yet" means wait; 202 means a fresh
// call is being signed. Ask at most every 15 seconds: the call has its own limit per address.
let answer;
for (;;) {
  const r = await post("/solana-self-call", { launchId, ticket });
  if (r.status === 200) { answer = r.body; break; }
  if (r.status === 202) { await sleep((r.body.retryAfterS ?? 5) * 1000); continue; }
  if (r.status === 409 && /not ready/.test(r.body.error)) { await sleep(15_000); continue; }
  throw new Error(r.body.error);
}

// 4. Sign the seed message again (for answer.nonce, the same nonce), build launch_self, sign it and send it.
// Send it well before answer.call.deadline: with less than a minute left, ask again for a fresh call.
const { transaction, size } = await selfSolanaLaunchTx({ signature: sign(seedMessageBytes(answer.nonce)), answer, mint: selfSolana.mint });
transaction.sign([creator]);
const signature = await connection.sendRawTransaction(transaction.serialize());
console.log(`sent ${signature}, ${size} bytes`);
const confirmed = await connection.confirmTransaction(
  { signature, blockhash: answer.blockhash, lastValidBlockHeight: answer.lastValidBlockHeight },
  "confirmed",
);
if (confirmed.value.err) throw new Error("the launch failed: ask for a fresh call and send again");

// 5. Optional: tell Pangea which transaction it was, so the launch moves on sooner.
await post("/solana-self-call", { launchId, signature });

// 6. Follow the event stream: pool-live for solana, then the other chains as Pangea sends them.

Launch-time buys

Pangea sends nobody’s buys. The launch kit gives you what you need to make your own, from your own wallets, and leaves the sending to you.

POST /v1/launch/kit with { ticket, buys, deadline } answers once the launch is paid and submitted; before, it is 409. buys is optional: at most 20 of { chain, amountIn, minOut }, EVM chains only, amountIn in the native coin’s wei and minOut in the token’s base units, both positive whole numbers as strings. deadline is optional too: unix seconds between 60 seconds and 1 hour from now, 1 hour when left out.

FieldWhat it is
launchId, stateThe launch, and where it stands in the queue.
evm[]Each EVM chain: chain, chainId, token, quote, factory, poolKey, poolId, router, the Universal Router, and live, whether its pool is open.
poolKey{ currency0, currency1, fee: 0, tickSpacing: 60, hooks }, with the native coin as the zero address and hooks the factory’s fee hook. poolId is keccak256(abi.encode(poolKey)).
buys[]{ chain, chainId, to, data, value, minOut, deadline }: a call of the Universal Router’s execute with one V4_SWAP, an exact-in single swap then SETTLE_ALL and TAKE_ALL. The tokens go to the wallet that sends it. value is your amountIn.
validUntilThe deadline every buy above carries, in unix seconds. Ask again for later ones.
selfLaunch{ chain, call } for a launch you send yourself: call is your launch call once it is out, and null until then.
solana{ available: false, reason } until Solana has launched, because its addresses are secret until then. Then { available: true, venue, programId, pool, mint, tokenDecimals, swap }: the pool is on Meteora DAMM v2, program cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG, and swap says how to swap on it.
solanaCosignedLaunch{ enabled: false } with the reason. Not available yet.

Buy calldata is built for pools paired with the native coin only; a pool paired with an asset is refused with 409. minOut is required: the pool does not exist before the launch, so there is no quote to take it from, and a buy that would get less reverts. Work it out from the opening price the quote gives, less the trading fee, your own buy’s price impact and the slippage you accept.

Ask for the launch kit and send a buy once its pool is open, with viem
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base, bsc, mainnet, robinhood } from "viem/chains";

const API = "https://www.pangea.trade/api/v1/launch";
const CHAINS = { 1: mainnet, 8453: base, 56: bsc, 4663: robinhood };

// The ticket prepare gave you, and a wallet to buy from: any wallet, and it receives the tokens.
const ticket = process.env.LAUNCH_TICKET;
const buyer = privateKeyToAccount(process.env.BUYER_KEY);

const res = await fetch(`${API}/kit`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  // 0.01 ETH on Base. At the opening price ($3,000 for a chain's 1B tokens) and ETH at $3,000 that is
  // about 10,000,000 tokens before the pool's fee and the price moving; ask for at least 9,000,000 (9 decimals).
  // Work minOut out from your own prices: it is the floor below which the buy reverts.
  body: JSON.stringify({ ticket, buys: [{ chain: "base", amountIn: "10000000000000000", minOut: "9000000000000000" }] }),
});
const kit = await res.json();
if (!res.ok) throw new Error(kit.error);

const [buy] = kit.buys;
const pool = kit.evm.find((c) => c.chain === buy.chain);
console.log(`pool ${pool.poolId}, token ${pool.token}, buy valid until ${kit.validUntil}`);
// A buy works only once its pool is open: wait for pool-live on the event stream, or ask again.
if (!pool.live) throw new Error(`the ${buy.chain} pool is not open yet`);

const wallet = createWalletClient({ account: buyer, chain: CHAINS[buy.chainId], transport: http(process.env.RPC_URL) });
const hash = await wallet.sendTransaction({ to: buy.to, data: buy.data, value: BigInt(buy.value) });
console.log("sent", hash);

Making your buys land with your launch

  • Ethereum. Sign your launch transaction and each buy, each buy from its own wallet with its own nonce and an explicit gas limit, since an estimate fails while the pool does not exist. Send them to a private relay such as Flashbots in one submission for one target block, your launch first. Nobody sees them before that block.
  • BSC. The same, through a private submission to 48 Club or bloXroute: all of them for one block, your launch first.
  • Base and Robinhood Chain. There is no public private relay. Send your launch, watch the event stream for pool-live on that chain, then send your buys. Others can buy in the same block.
  • Solana. Pangea’s launcher sends Solana’s launch. Once it has landed, the kit gives its pool and mint, and you swap on Meteora like anyone. A launcher-signed Solana launch that you could submit through Jito with your buys after it is not available: POST /v1/launch/solana-cosigned answers 409.
Every buy that lands in a chain’s launch block, yours or anyone’s, from any wallet, is shown publicly on the token’s page under “Bought in the launch block”: the launch block only, as the trade recorder read it, so buys in later blocks -- on Base and Robinhood Chain, where you buy after the pool opens, usually the next block or two -- are not listed there. Buys inside the launch transaction itself are shown as “In the launch tx”. Solana is not covered: its launch has no recorded slot.

Risks

  • Without a private relay, a buy can be front-run in the same block. minOut is what protects you: below it, the buy reverts.
  • A signed snapshot expires: its deadline is never more than 1 hour away, and normally 15 minutes. After it your launch call stops working, whoever sends it.
  • If your launch transaction lands after Pangea’s takeover, it reverts and you lose only its gas. The launch is made either way, with the same price and settings.
  • A buy sent before its pool is open reverts, and costs its gas.
E.6

Bundle it yourself

E.6

Pangea's launcher launches your token on every chain, as for any launch. With the bundle kit, on each chain that has private bundles you get our own signed launch transaction for a short hold (20 seconds by default): put it in a bundle of your own with your buys right after it, and the launch and your buys land together. You pay everything: the bundle's gas and tip, the gas of our held launch, your buys, and the usual launch fee. Pangea assembles no bundle, signs nothing of yours and keeps no gas of its own on the held transaction.

Two ways to bundle on Ethereum or BSC. Here: along OUR signed launch transaction, as below. Or with "Launch it yourself": you send each EVM launch from your own wallet and bundle it however you like.

ChainHow your buys go along the launch
SolanaFirst: our signed launch_v2 transaction, first in your own Jito bundle; your own transaction after it pays the Jito tip
EthereumOnce Solana is live, a Flashbots or Titan bundle of three: your transaction funding our hold key's gas, our signed launch, your buys
BSCOnce Solana is live, a 48 Club or BlockRazor bundle of three: your transaction funding our hold key's gas, our signed launch, your buys
BaseNo private bundles: your buys are made by contract in our launch transaction itself (the built-in multi-wallet launch buys)
Robinhood ChainNo private bundles: your buys are made by contract in our launch transaction itself (the built-in multi-wallet launch buys)

How to use it

  • Prepare the launch with bundleKitHeld: true (a hold key is reserved for you on Ethereum and BSC: the quote then charges each such chain the gas to clear the hold key if a hold lapses, 52,500 gas, and NOT that chain's launch transaction's gas -- its launch, launch buys, dev buy and fee check -- which your bundle pays; a chain with no free hold key, and the chain you pay on (never held), is quoted and launched un-held, and bundleKitHeld.unheld names it), pay the fee on any chain and submit with bundleKit: true (POST /api/v1/launch/submit). Base and Robinhood Chain buys: name them as launch buys when you prepare the launch.
  • Poll POST /api/v1/launch/bundle-kit with { ticket }. A held chain shows state: "held", holdUntil (unix seconds) and tx: tx.raw (the raw signed transaction) on Ethereum and BSC, tx.wire (base64, a v1 transaction) on Solana. Only your ticket gets it, and only while it is held.
  • Solana: put our transaction FIRST in your Jito bundle, your tip transaction after it, before holdUntil.
  • Ethereum and BSC (once Solana is live): our transaction is signed by a hold key that holds no gas. Your bundle is three transactions, in this order: (1) yours, sending fund.wei (shown with it) to fund.to, the hold key; (2) ours, tx.raw; (3) your buys. Send it before holdUntil.
  • Not landed by holdUntil on Ethereum or BSC: the kit shows state: "lapsed" -- that chain is NOT launched (our launcher never sends a held leg itself: its launch gas is your bundle's, not in the fee). The hold key's nonce is burnt (the 52,500 gas in your fee), and you are refunded that chain's share of the fee, and the route fee you paid for that chain if you ticked "Open all bridge routes at launch". Solana: state: "sent", our launcher sends exactly the held transaction. A bundle sent after the hold may revert, at your gas.
  • Live: state: "live". Solana shown state: "ours" could not be held (its transaction could not be signed): our launcher sends it, as for every launch. An Ethereum or BSC leg quoted held is never sent by our launcher: one that could not be held at launch (for example, every hold key was in use) is state: "lapsed", as above. state: "checking": the chain could not be read after its hold -- it is read again, and is then live or lapsed.
  • The chain you pay on is never held (owner rule): paid on Ethereum or BSC, that chain is launched by our launcher as usual, un-held, with its launch gas in the fee, and you cannot bundle on it. Instead you can bundle on your other chains, use "Launch it yourself" and send that chain's launch from your own wallet, or pay on another chain. Prepare's bundleKitHeld.payChain says so, and the launch page shows it before you pay.
  • A lapsed chain is not launched, and nothing is bridged to it: it is left out of the coin's chains, its routes and every bridge quote.
Holds never slow anyone else: each held launch uses its own hold key, apart from the launcher every other launch uses, and a chain quoted with no hold key free goes out un-held, as any launch. Not with a launch whose legs you send yourself (selfLaunch, selfSolana, or "Launch it yourself"). The fee is the launch's usual fee plus the hold's worst-case gas on each Ethereum and BSC leg (a few tens of thousands of gas).
Sheet GEarlier (v1)
G.1

How it works

G.1

Before factory v2. Every launch deployed the same token on all five chains at once. Each chain got 1B tokens in its liquidity pool, 5B in all, and all five pools opened at the same US dollar price: $3,000 of market cap a chain.

Every token went into its liquidity pool from the start. There is no team allocation, treasury or vesting. Each pool started with tokens only and collects the quote asset as people buy.

Each chain operates independently, and one thing keeps the five prices together: arbitrage, which is open to anyone. Buy on a cheaper chain and sell on a more expensive one.

What a v1 coin does not have

These came with factory v2, and a v1 coin has none of them: a launch on selected chains, a chosen opening market cap, degen mode, a different trading fee on each chain, a fee receiver apart from the creator wallets, and coin tokenomics. How v2 works →

The v1 factory

ChainLaunch factory (v1)
Ethereum0x1D583e6eA4AA7c5e167a7E69B07b83DD5B813f53
Base0xba4beE425F268f7Dc0F7e5457d14320d5A64cd06
BSC0xd1454764E8D87B117a23491E77cAfcCC50430256
Robinhood Chain0x8A1F3e1DB07048c007F6d9ab924494C77c0DBa65

Nothing new launches from it. The coins it launched trade, bridge and pay their fees as this tab describes.

G.2

The five pools

G.2

Each chain got 1 billion tokens in its liquidity pool, held in a concentrated liquidity position. As the price rises, the pool sells tokens and collects the quote asset.

ChainVenueQuote
EthereumUniswap v4ETH
BaseUniswap v4ETH
Robinhood ChainUniswap v4ETH
BSCUniswap v4BNB
SolanaMeteora DAMM v2SOL

Each pool pays sellers from its own reserves. A chain with fewer buyers may have less quote asset available, regardless of how much liquidity exists elsewhere.

Opening market cap

Every v1 coin opened at $3,000 a chain, $0.000003 a token, on all five chains at once. It was fixed by the contracts, not chosen.

G.3

Five contracts, one token

G.3

Each chain has its own token contract and address, so buyers choose which chain to trade on.

A token’s address is different on every chain (unless its creator chose “Same contract address on all EVM chains”, which puts it at one address on the four EVM chains), and every one of them ends in 6ea: the last three hex digits on the four EVM chains, the last three characters of the mint on Solana. The token’s page lists every one.

Each chain has 1 billion tokens in its own liquidity pool, so 5 billion across the five chains.

One launch, five contracts
Ethereum1BIn its pool, own address
Base1BIn its pool, own address
Robinhood1BIn its pool, own address
BNB Chain1BIn its pool, own address
Solana1BIn its pool, own address

Each contract reports its own chain’s 1B supply.

G.4

The market cap situation

G.4

Terminals and apps calculate market cap in different ways. A token’s full market cap is the sum of its market caps on all five chains.

The price is the same on every terminal and app. You can bridge your tokens, or buy on any of the five contracts, at the same dollar price for the same number of tokens.

To see a token’s complete market cap, use its page on this site: it is the most accurate source.

The differences between platforms come from how Pangea manages supply on each chain to keep prices in line. That is what keeps the price gap between chains small, and what makes a single token across many chains possible. Every platform is consistent with itself, and many of them show the same figures.

The key point: the same token, at the same price, on every chain.

G.5

Trading fee

G.5

Each v1 launch set a trading fee between 0.3% and 5%, once, at launch, the same on all five chains. No contract function or program instruction changes it afterwards.

The platform takes 20% of the trading fee, with a floor of 0.2% of volume. The creator receives the rest.

Trading fee
1.00%
Platform
0.20%
(20%)
Creator
0.80%
(80%)

Percentages of volume. In brackets, each side's share of the trading fee itself.

Trading feePlatformCreator
0.3%0.2% (floor)0.1%
1%0.2%0.8%
5%1.0%4.0%
The minimum fee is 0.3% because Meteora keeps 20% of trading fees on Solana. At a 0.2% fee, only 0.16% of volume would remain, less than the platform’s share. This also means creators earn less on Solana: at a 1% trading fee they receive 0.6% there against 0.8% on Base.

80% of platform fees go to PANGEA buybacks.

PANGEA buybacks
80%
Platform
20%

Of platform fees.

Who receives the creator’s share. The creator wallets the launch named: one EVM address for the four EVM chains and one Solana address for Solana. On Solana, the fee split is enforced by Pangea's splitter program.

Where the fee goes can change. Either side may hand its stream to another address, in two steps — the new address has to accept it — so a project that changes hands does not have to relaunch to move its revenue with it.

G.6

Fee collection

G.6

Fees are paid in the quote asset: ETH, BNB or SOL. Never in the token that was launched.

Meteora supports collecting fees directly in the quote asset. Uniswap v4 collects fees in whichever token enters a swap, so fees from sells accrue in the launched token.

To convert them, the platform swaps those tokens for the quote asset when fees are claimed. This keeps extra code off the trading path, reducing the risk of a bug disrupting swaps. The trade-off is a small price impact from selling the collected tokens into the pool.

G.7

PANGEA tokenomics

G.7

$PANGEA is live. It launched through Pangea on all five chains, with the same five-chain structure as every other token. Trade $PANGEA →

ChainOfficial $PANGEA contract
Ethereum0x6434e57e8d12B92bb4D0b1EFb3274748B75196ea
Base0xA551dEF8a44ea423CaA58DA45b3F5764004286Ea
Robinhood Chain0x30de7AE4c9578D110a97367EDe9F7765Cdce06ea
BSC0xd0cb4279e2B74c2d72EEa20Df308Fb9604E3d6Ea
SolanaJAFqCQSpy5YinrsmixWSMaD7jTn4MytJRkyWvYPnY6ea
These five are the only official $PANGEA contracts. Any launch may use the ticker PANGEA; a token at any other address is not this one.
G.8

Team allocation

G.8

The team holds about 19% of the total supply of $PANGEA, which is 5B summed across the five chains. Most of it is locked: about 14.4% sits in vesting contracts and is released in equal monthly steps over two years. The remaining 4.18% is a marketing treasury. Nothing is held in an anonymous wallet: every address below can be checked on chain.

0.0%Team
  • Locked team allocation14.4%
    Released over 24 months718M
  • Marketing treasury4.18%
    For marketing and growth209M
  • Everyone else81.5%
    Pools, holders, the market4073M
Release schedule~0.60% of supply / month
Month 1Month 12Month 24
Where it is locked
EthereumTeam Finance
222.9M
RobinhoodTeam Finance
228.8M
BNB ChainTeam Finance
55.0M
SolanaStreamflow
211.0M

Locked allocation

The locked tokens are not in a team wallet. They sit in Team Finance lock contracts on Ethereum, Robinhood and BSC, and in a Streamflow vesting stream on Solana. Each releases in equal monthly steps over 24 months, about 0.6% of the total supply a month across the four. No one can withdraw ahead of that schedule: the three Team Finance locks have no owner (it was handed to 0x…dEaD on 2026-10-24, which ends their emergency withdrawal and revocation), and the Streamflow stream cannot be cancelled by its sender or its recipient.

ChainLockLocked
Ethereum0xbfae7682f5550d0a9572f750906c4c4e54502ee1222.9M
Robinhood0xBe17cDf528aBb0797B8DD8e3d47E43A2bAF9cb34228.8M
BSC0xc362b1d31bc07712b9eba15755daeaed47a0b0c155.0M
Solanaou8M2oj3byMTRmaxispdbh82pKmew7NLDt6Yy36R6zW211.0M

Marketing treasury

The treasury funds marketing and growth. It is one address on every EVM chain, 0x402908cd6eB5176A1B9FdE3690CCccF41d7D54D0, named pangeatreasury.eth, pangeatreasury.base.eth and pangeatreasury.bnb.

Balances were read on chain when this was written; the explorers (Etherscan, Robinhood's Etherscan, BscScan and Solscan) show them live, along with each lock's release schedule.
G.9

Bridging

G.9

The token uses LayerZero OFT. Transfers burn tokens on the source chain and mint them on the destination, keeping the total across the five chains unchanged. There are no wrapped tokens.

Each chain’s pool received its 1 billion tokens at launch, so all five pools opened at once.

Worked exampleevery chain starts on its 1000M slice
Burn on
Mint on
250M
Ethereum1000M
Base750M
Robinhood1000M
BNB Chain1000M
Solana1250M
Across the five chains5000M

Standard or fast

A standard transfer takes LayerZero’s own path. On every route the verifiers attest once the source transaction has 1 confirmation, and a transfer typically arrives in 4 to 16 seconds. From Ethereum, whose blocks are slower, it takes longer. How to bridge, step by step →

A fast transfer arrives in seconds: a relayer pays the recipient at once from its own tokens and is repaid when the standard transfer lands. It costs the fast lane’s own 0.2% on top of the standard 0.2%, plus the relayer’s price, which is capped at 10%. The bridge screen offers it only when a relayer is quoting your transfer. Relayers →

What a transfer costs

  • The token’s fee. The token keeps 0.2% of every transfer between chains. It goes to the platform. On the four EVM chains it is fixed in the token contract and nobody can change it. On Solana, the fee split is enforced by Pangea's splitter program.
  • The message fee. LayerZero’s price for carrying the transfer, paid in the source chain’s native asset. It is quoted for the exact transfer before you sign.
  • A Solana route’s setup. The first transfer on a Solana route nobody has used yet also pays a one-time setup fee for that route. Every later transfer on it does not.

A transfer of 1,000 tokens: 2 go to the platform and 998 arrive.

Transfers are rounded down to six decimal places. Amounts smaller than 0.000001 token are dropped.
G.10

How to bridge

G.10

Bridging moves your tokens from one chain to another. It is the same token on both sides: the tokens are burned where you send them from and minted where they arrive, so nothing is wrapped and the total across the five chains does not change.

On the Bridge page

  • Pick the token. Open Bridge in the menu and choose the token, or open a token’s page and use its bridge tab. A link with the token in it, such as /app/bridge?token=ptest4, opens it ready.
  • Pick From and To. The chain your tokens are on now, and the chain you want them on. The swap button between them turns the route around.
  • Connect a wallet. An EVM wallet for Ethereum, Base, Robinhood Chain and BSC, a Solana wallet for Solana. A route between an EVM chain and Solana needs both: one sends, the other receives.
  • Enter the amount. Your balance on the From chain is read from the chain. The page shows what you send, the token’s fee, what arrives, and the message fee, before you sign anything.
  • Choose Standard or Fast. Standard is the token’s own path. Fast appears only when a relayer is quoting your transfer.
  • Sign. One transaction on the From chain for a standard transfer. A fast transfer may ask for an approval first. The first transfer on a Solana route that nobody has used yet may ask for a setup step first; see below.
  • Wait for it to arrive. The page follows the transfer and says when it has landed. Recent transfers are listed under the card.

What it costs

  • The token’s fee, 0.2%. Kept by the token for the platform, out of the amount you send. 1,000 tokens sent, 998 arrive. (A token from before factory v3. A factory v3 token's fee is its chain's registry figure, 0.2% unless changed, at most 0.5%.)
  • A factory v3 token: the price-gap fee. A transfer from the chain where the token is cheaper to the chain where it is dearer also pays the price gap, burned at the destination. The quote shows the estimate before you sign; the other way is free.
  • LayerZero’s message fee. Paid in the From chain’s native asset: ETH on Ethereum, Base and Robinhood Chain, BNB on BSC, SOL on Solana. It pays the verifiers and the delivery on the other side, and it is quoted for your exact transfer before you sign.
  • A factory v3 token into Ethereum: Pangea's delivery. LayerZero's executor does not deliver there, so Pangea's delivery keys land the transfer; their cost is part of the network fee, in the From chain's coin (out of Solana, in SOL), and the quote shows it as its own line (Pangea delivery).
  • A factory v3 token into Solana: the Solana delivery. Pangea's delivery key lands the transfer on Solana; its cost is withheld from what arrives, in the token, and shown as its own line (Solana delivery). A new wallet's first arrival also pays its token account's rent from it.
  • A transfer too large for one send: every part pays. Sent in parts, each part is its own transfer and pays the full network fee (LayerZero, and any delivery above); the quote shows the fee per part and the total for all parts before the first signature. A co-signed transfer (one send) shows its fee and its guaranteed minimum before you sign it.
  • Gas. The transaction itself, on the From chain, as for any transaction there.
Route, 1,000 PTEST4Message fee quoted
Base to BSC0.000110 ETH
Ethereum to Base0.000205 ETH
Base to Solana0.000425 ETH
Solana to Base0.00058 SOL

Those are live quotes from 19 October 2026, to show the size of the fee; it moves with gas prices. A transfer into Solana costs more because it also pays for the token account the tokens arrive in.

How long it takes

A standard transfer waits for its transaction to have 1 confirmation on the From chain, then for two verifiers to attest it, and is then delivered. Measured on mainnet, transfers between Base, BSC, Robinhood Chain and Solana took 4 to 16 seconds:

RouteSeconds, sent to deliveredTransfers measured
Base to BSC52
Base to Robinhood Chain4 to 54
Base to Solana5 to 74
BSC to Base81
Robinhood Chain to Base10 to 164
Robinhood Chain to BSC71
Robinhood Chain to Solana5 to 132
Solana to Base12 to 154

From Ethereum it takes longer. Ethereum makes a block only every 12 seconds, and the verifiers wait for the transfer’s block to be confirmed before they attest, so expect about half a minute or more. No transfer out of Ethereum has been measured on mainnet yet, and the Bridge page currently shows the same estimate for every route.

The fast lane

Fast is an option on top of the standard transfer. When you choose it, the transfer carries a public order on Pangea’s fast bridge, PangeaFastBridge, one shared contract per chain. A relayer sees the order and pays you on the To chain within seconds from its own tokens, and is repaid when your standard transfer lands. It costs the fast lane’s 0.2% and the relayer’s price on top of the standard 0.2%, and the price is fixed in the order before you sign. If nobody fills the order, your tokens still arrive the standard way and you get the relayer’s fee too. How relayers work →

  • Fast appears only when a relayer is quoting your route and amount. Otherwise only Standard is offered.
  • Into Solana it needs the token to allow a 232-byte message. Tokens launched from the current factories allow 256; some earlier launches allow 200, and for those the page offers Standard only into Solana.
  • A fast transfer may need an approval first: the fast bridge moves the tokens for you, once.

Solana

  • Into Solana, the recipient is a wallet. Name the Solana wallet that will own the tokens, not a token account. The tokens arrive in that wallet’s token account for the token, which is created on delivery if it does not exist.
  • That account’s rent is in the message fee. Every transfer into Solana carries 2,074,080 lamports (about 0.002 SOL), the rent of a new Token-2022 account, paid in the From chain’s asset as part of LayerZero’s fee. You need no SOL to receive. For a factory v3 coin today, the first arrival to a new wallet instead has that rent withheld from the tokens that arrive (the quote’s gap.delivery.ata), and an arrival too small to pay it waits on Solana until the account exists (gap.warning).
  • The first transfer on a route pays its setup. A token’s route between Solana and a chain is opened the first time anyone uses it. That first transfer also pays a one-time setup fee: the rent of the accounts LayerZero keeps for the route. The page shows it as a separate line and may ask for two steps. Every later transfer on that route, by anyone, does not pay it.
  • Out of Solana, you pay in SOL. The message fee and the transaction fee are paid from your Solana wallet, so keep a little SOL there.
  • Amounts cross at six decimals. The odd base units below 0.000001 token stay in your wallet.

If a transfer seems stuck

  • Look it up on LayerZero Scan. Paste the transaction hash or signature of your send at layerzeroscan.com. It shows each verifier’s attestation and the delivery, with links to both transactions.
  • Inflight or confirming. The verifiers are waiting for the confirmation or attesting. Wait: this is the ordinary state for the first seconds, and longer from Ethereum.
  • Verified but not delivered. Delivery is open to anyone once a message is verified. LayerZero’s executor delivers it, and Pangea runs its own delivery service that delivers every Pangea token’s messages as soon as they are verified. If one sits verified and undelivered for more than a few minutes, anyone can deliver it by calling the destination endpoint’s lzReceive with the message, at their own gas cost. The exception: a factory v3 token's charged arrival into Solana, which only Pangea's appointed deliverers may land.
  • Into Solana, on a route that is not open. A transfer sent into Solana before its route is opened waits, and is delivered once the route is opened; LayerZero Scan may show it as blocked until then. Nothing is lost in between. The Bridge page opens the route as part of the first transfer on it.
  • A held fast order. When a fast order cannot be paid on the To chain, the fast bridge holds it for the sender. The Bridge page shows a button that sends it back; anyone may do it, paying LayerZero’s fee for the trip back.
  • Nothing on LayerZero Scan. The send did not go through on the From chain. Check it on that chain’s explorer: if it failed, the tokens never left your wallet.
G.11

LayerZero routes

G.11

One token, five contracts

  • An address per chain. A token is a LayerZero OFT contract on each of Ethereum, Base, Robinhood Chain and BSC, and a mint on Solana, held by LayerZero’s OFT program. The five addresses are all different (or, with “Same contract address on all EVM chains”, one on the four EVM chains and the mint on Solana) and all end in 6ea. They are on the token’s page and in /api/pools.
  • 1B per chain. Each chain mints its own 1 billion at launch, all of it into that chain’s pool, 5 billion across the five. On an EVM chain launchSlice() is that 1B and launchTotal() the 5B (on a launch on fewer chains, 1B for each of them). Nothing can mint more: there is no mint function and no minter. In degen mode each chain mints its equal share of 1B instead, and launchTotal() is the 1B.
  • Burn and mint. A transfer burns the tokens on the chain it leaves and LayerZero’s message mints the same number on the chain it reaches. On an EVM chain the token’s fee is moved to bridgeFeeTo() and only the rest is burned, so what is burned on one side is exactly what is minted on the other. A factory v3 token's price-gap fee is the exception: the destination withholds it and never mints it.
  • What is on a chain, and what is reported. On each EVM chain localSupply() is what is on that chain now; it moves as tokens cross. totalSupply() is a reported figure: it starts at the chain’s 1B, and for tokens with a SupplyReporter the platform’s reporter on that chain mirrors the token’s supply on Solana into it. A report moves no balance and mints nothing, and a factory v1 token refuses one below its own localSupply() or above launchTotal(). Earlier launches have no reporter and always report their slice. A degen mode token reports the 1B total and takes no reports.
  • Peers are sealed at launch. A peer is the token’s counterpart on another chain, the only address it accepts a mint from. The factory registers the four peers in the transaction that creates the token and hands ownership to a LaunchOwner contract that is sealed from the start: sealed_() is true and it has no way to add, change or remove a peer.
ChainSupplyReporter
Ethereum0xD7AF0c412911DCE5F864d949abA14a3048DD87B2
Base0xF10254C126e41F69799e781D185cc42c99EE37Ed
Robinhood Chain0x51D658a71AEa38f123d4c73e42703Fb9C949900A
BSC0xf1a98Fa0B2baB64BC4358dC0C2c13444ecCA365b

How every route is set up

A route is one direction between two chains, for one token. When the factory creates a token it is, for that one transaction, the token’s LayerZero delegate, and it writes every route from a table fixed in its own constructor. Nothing is left on LayerZero’s defaults, because a default can be changed by whoever administers the endpoint.

  • Libraries. The send and the receive message library are named explicitly: LayerZero’s Ultra Light Node on every chain, the same contracts the endpoint’s defaults point at today, pinned so they stay.
  • Executor. LayerZero’s executor on each chain is named explicitly, with a cap on the message size.
  • Verifiers. No required verifiers. An optional set of Pangea’s own verifier plus the external ones, and 2 of the set must attest before a message counts. So no single party can deliver a message: not Pangea’s verifier, and not any one external one.
  • Confirmations. 1 on every source chain, Solana included: the verifiers attest once the send has 1 confirmation. Both ends of a route hold the same number.
  • A gas floor. Every transfer must give the delivery at least 100,000 gas on an EVM chain, and 200,000 compute units plus 2,074,080 lamports on Solana, for the recipient’s token account. It is an enforced option, added to every send, so a sender cannot under-pay delivery.
  • A message size. Up to 10,000 bytes between EVM chains and from Solana. Toward Solana 256 bytes for launches from the current factories; the factory admin can move that between 200 and 800 for launches after, never for a token that exists. A Solana transaction is small, and a cap above what Solana can deliver would let a message be sent that can never land.
ChainOptional verifiersOf which must attestConfirmations
EthereumPangea, LayerZero Labs, Nethermind, Horizen, Canary21
BasePangea, LayerZero Labs, Nethermind, Horizen, Canary21
Robinhood ChainPangea, LayerZero Labs, Nethermind21
BSCPangea, LayerZero Labs, Nethermind, Horizen, Canary21
SolanaPangea, LayerZero Labs, Nethermind, Horizen, Canary21

That is the set on each chain, for its routes to Ethereum, Base, BSC and Solana. Robinhood Chain has two of the external verifiers, so a route with Robinhood Chain at either end uses the verifiers both chains have: Pangea, LayerZero Labs and Nethermind, 2 of 3. Every other route has 5, 2 of 5.

ChainSend libraryReceive libraryExecutor
Ethereum0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe10xc02Ab410f0734EFa3F14628780e6e695156024C20x173272739Bd7Aa6e4e214714048a9fE699453059
Base0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda20xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4
Robinhood Chain0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de70xe1844c5D63a9543023008D332Bd3d2e6f1FE10430x4208D6E27538189bB48E603D6123A94b8Abe0A0b
BSC0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC10x3ebD570ed38B1b3b4BC886999fcF507e9D584859
Solana7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVHAwrbHeCyniXaQhiJZkLhgWdUCteeWSGaSN1sTfLiY7xK
ChainLayerZero endpointLaunch factory (v2)Earlier launches (v1)Pangea’s verifier
Ethereum0x1a44076050125825900e736c501f859c50fE728c0xf7052a4E38e241bc66bfAe6eB27D10B2A42cacB40x1D583e6eA4AA7c5e167a7E69B07b83DD5B813f530x5FA03555f186e53a644405817E1BeAC894Bd6562
Base0x1a44076050125825900e736c501f859c50fE728c0xb405b396b607C3482A3d301b1cFF57A7de75325a0xba4beE425F268f7Dc0F7e5457d14320d5A64cd060x6dbFda44baB6bf0DDF3E9F88d7823eDdB48f4b25
Robinhood Chain0x6F475642a6e85809B1c36Fa62763669b1b48DD5B0x9Cf5832474CBB09D2E06FF20460aff4303fAf72A0x8A1F3e1DB07048c007F6d9ab924494C77c0DBa650x85880e7D5315365165E97620Ba17B48A645b13F3
BSC0x1a44076050125825900e736c501f859c50fE728c0x92858095Cadf9f9d60dC17226f7FF15Be4b69e450xd1454764E8D87B117a23491E77cAfcCC504302560xF61Ed4347c4D02522e96657a606f49E8381B3dCA
SolanaULN 7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVHSplitter program Hb5uTppRbNxc3RjVRqTWqfdU3E3GiZyGYKccxYf9tUejThe same program7EgbAY7663cybirqgmFHzF8q9debeePxr8ra7wKyEmNE
VerifierEthereumBaseRobinhood ChainBSCSolana
LayerZero Labs0x589dEDbD617e0CBcB916A9223F4d1300c294236b0x9e059a54699a285714207b43B055483E78FAac250xd01ae6905d48315f7bE10C7330aeCF8360Ef5b120xfD6865c841c2d64565562fCc7e05e619A30615f04VDjp6XQaxoZf5RGwiPU9NR1EXSZn2TP4ATMmiSzLfhb
Nethermind0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA50xcd37CA043f8479064e10635020c65FfC005d36f60x0Ffe02DF012299A370D5dd69298A5826EAcaFdF80x31F748a368a893Bdb5aBB67ec95F232507601A73GPjyWr8vCotGuFubDpTxDxy9Vj1ZeEN4F2dwRmFiaGab
Horizen0x380275805876Ff19055EA900CDb2B46a94ecF20D0xa7b5189bcA84Cd304D8553977c7C614329750d99not on this chain0x247624e2143504730aeC22912ed41F092498bEf2HR9NQKK1ynW9NzgdM37dU5CBtqRHTukmbMKS7qkwSkHX
Canary0xa4fE5A5B9A846458a70Cd0748228aED3bF65c2cd0x554833698Ae0FB22ECC90B01222903fD62CA4B47not on this chain0xfA9bA83C102283958B997Adc8B44ED3A3CdB5dDa7jMeX5mzXnSSKYd8DxBDP4xMnkNFZZZm5W28FWUTbwU3

On Solana the same is done by Pangea’s splitter program. The program holds one route setting per destination chain, and the first transfer on a token’s route applies it to that token, once: the libraries, the executor, the verifiers in both directions, the confirmations and the gas floor. The token’s LayerZero admin and delegate on Solana is an address only that program can sign for, and the program has no instruction that changes a peer, a library or a verifier afterwards.

Why nobody can change a route later

  • On an EVM chain the factory writes the routes while it is the token’s delegate, then never calls the endpoint for that token again and has no function that could. The token’s owner, the sealed LaunchOwner, has no way to name another delegate. So no key, including Pangea’s, can change a token’s libraries, executor or verifiers.
  • This matters because a delegate who could name the verifiers could name itself, attest a message nobody sent, and mint. Sealing the peers alone would not close that.
  • One thing stays adjustable on purpose: the gas floor. The LaunchOwner’s admin can raise or lower it, so that a chain changing its gas costs does not break bridging for good. It accepts exactly one lzReceive option and nothing else, under hard caps of 1,000,000 gas and 10,000,000 in value, so it cannot add a payment to anyone or set a floor nobody can pay. On Solana the gas floor is fixed when the route is opened.
  • The other side of this: if LayerZero retires a verifier a route depends on, that route keeps working only while two of its verifiers still attest, and nobody can swap one in. A paused bridge can be lived with; a token anyone could mint could not.
  • On Solana the admin can change the route setting for launches still to come. A token’s route is applied with the setting in force when it launched, and the program refuses a setting edited after that, unless the admin signs the opening; once a route is open nothing changes it.

Check it yourself

Everything above can be read from the chains, with no help from Pangea. The worked example is PTEST4, a live test launch, on Base. Its address there is 0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea; the other addresses are in the table at the end. These are read-only calls, with Foundry’s cast and any RPC endpoint for the chain.

The token's peers, owner and delegate, on Base
TOKEN=0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
ENDPOINT=0x1a44076050125825900e736c501f859c50fE728c
RPC=your-base-rpc-endpoint

# its counterpart on Ethereum, BSC, Robinhood Chain and Solana
for EID in 30101 30102 30416 30168; do
  cast call $TOKEN "peers(uint32)(bytes32)" $EID --rpc-url $RPC
done

# the owner is a LaunchOwner, sealed: true
OWNER=$(cast call $TOKEN "owner()(address)" --rpc-url $RPC)
cast call $OWNER "sealed_()(bool)" --rpc-url $RPC

# the delegate, the only key that can set verifiers, is the factory that launched it
cast call $ENDPOINT "delegates(address)(address)" $TOKEN --rpc-url $RPC
One route's libraries, executor, verifiers and gas floor: Base to Robinhood Chain
EID=30416
SEND_LIB=$(cast call $ENDPOINT "getSendLibrary(address,uint32)(address)" $TOKEN $EID --rpc-url $RPC)
cast call $ENDPOINT "isDefaultSendLibrary(address,uint32)(bool)" $TOKEN $EID --rpc-url $RPC
cast call $ENDPOINT "getReceiveLibrary(address,uint32)(address,bool)" $TOKEN $EID --rpc-url $RPC

# verifiers: (confirmations, required count, optional count, threshold, required, optional)
ULN=$(cast call $ENDPOINT "getConfig(address,address,uint32,uint32)(bytes)" $TOKEN $SEND_LIB $EID 2 --rpc-url $RPC)
cast decode-abi "f()((uint64,uint8,uint8,uint8,address[],address[]))" $ULN

# executor: (max message size, executor)
EXE=$(cast call $ENDPOINT "getConfig(address,address,uint32,uint32)(bytes)" $TOKEN $SEND_LIB $EID 1 --rpc-url $RPC)
cast decode-abi "f()((uint32,address))" $EXE

# the gas floor every send carries
cast call $TOKEN "enforcedOptions(uint32,uint16)(bytes)" $EID 1 --rpc-url $RPC

What those return for this token, read on 19 October 2026:

ReadAnswerWhat it means
peers(30416)0x00000000000000000000000068906d8b2a22d329b853ec650fa6a29fab6056eaThe token on Robinhood Chain, left-padded to 32 bytes.
peers(30168)0xbad1e830d4a3bba670d642dd253151f5564074eb91e9197e0216ec274e91df6eIts OFT store on Solana, DaGUXY1qixx3Wsp7KFb7L6UtXV6QHCRXtaXtx4yfLued, as 32 bytes.
sealed_()trueNo peer can ever be added, changed or removed.
delegates(token)0x025f893Bdcd7978232C211e3A28610FC6d8B8970The factory that launched it, an earlier one than today’s, with no code to touch the endpoint again.
isDefaultSendLibraryfalseThe library is pinned, not inherited.
getConfig(... 2)(1, 0, 3, 2, [], [3 verifiers])1 confirmation, no required verifiers, 3 optional, 2 must attest.
getConfig(... 1)(10000, 0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4)Messages up to 10,000 bytes, LayerZero’s executor.
enforcedOptions(30416, 1)0x000301001101000000000000000000000000000186a0One lzReceive option: 0x186a0, which is 100,000 gas.

The three optional verifiers it returns are Pangea’s on Base, 0x6dbFda44baB6bf0DDF3E9F88d7823eDdB48f4b25, and LayerZero Labs’ and Nethermind’s there. Toward Solana the same read returns 5 verifiers and an executor cap of 200 bytes: this token was launched before the factories raised it to 256. The gas floor toward Solana reads 0x00030100210100000000000000000000000000030d40000000000000000000000000001fa5e0: 0x30d40 is 200,000 compute units and 0x1fa5e0 is 2,074,080 lamports.

On LayerZero Scan, look up the token’s address, or any transfer. Its transfer from Base to Robinhood Chain on 16 October 2026, 0x5860a6920fd6fe6d97f7d88104d6ce105bb43fd4a9654d7d84cdd9229e961f65, shows all three of the route’s verifiers attesting, and the delivery five seconds after the send. Each message lists the verifiers that attested and the lzReceive gas it carried.

On Solana, a token’s route to a chain is open when LayerZero’s receive config account for it exists: the ULN program’s address for the seeds "ReceiveConfig", the chain’s endpoint id as four big-endian bytes, and the OFT store. Its contents are the confirmations and the verifiers, in the same order as above. For PTEST4, read on 19 October 2026, the routes from Base and Robinhood Chain are open, each with 1 confirmation and 2 of its verifiers, and the routes from Ethereum and BSC are not open yet: nobody has bridged on them. Bridging from code → has a script that checks it.

ChainPTEST4 on that chain
Ethereum0x835e9CFB333B9E4811CA1585460442806a99d6eA
Base0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
Robinhood Chain0x68906D8b2A22D329b853ec650Fa6A29fAb6056Ea
BSC0x90Ba4Df685A2D33c12C40F03808F290519E7F6EA
SolanaMint DdJ6R9TSKdNYMZhdgATANT98BJNgScuU58baQoCfZ6ea, OFT store DaGUXY1qixx3Wsp7KFb7L6UtXV6QHCRXtaXtx4yfLued
Pangea’s own checks do the same against every factory before a launch: each route’s two ends must agree, and every verifier a factory names must be on a list kept by hand, with the operator’s name. The addresses on this page are those.
G.12

Relayers

G.12

A relayer pays the recipient on the destination chain within seconds, out of its own stock of the token, and is paid back, plus its fee, when the bridged tokens arrive over the standard path a few seconds later. Nobody has to trust it: the recipient, the amount and the fee are fixed by the order, and a relayer that pays anything else is simply never repaid. If no relayer fills an order, the transfer arrives the standard way, and the fee no relayer earned goes to the recipient.

There is one fast bridge per chain, shared by every token launched on Pangea. A new token can use it the moment its launch lands: nothing is deployed for it. An order names its launch, never a token address, and the bridge reads the token itself from Pangea’s own factories, so a relayer is only ever repaid in, and a recipient only ever paid in, that launch’s own token on that chain.

The fast bridges on Ethereum, Base, Robinhood Chain and BSC are deployed, at the addresses listed at the end of this section. Orders out of Solana are posted by Pangea’s fast-bridge program, in the same format. Into Solana the fast lane needs a token that allows a 232-byte message, which every launch from the current factories does.

What a transfer costs, and who is paid

  • The token’s fee. The token keeps 0.2% of every transfer between chains, on either lane. It goes to the platform.
  • The fast lane’s fee. On the fast lane the fast bridge keeps 0.2% first, also for the platform, and the token’s fee is then taken from what is left. The bridge’s admin can change it, per launch or for all, but never above 1%.
  • The relayer’s fee. This is a price, not a platform fee. Relayers publish what they charge and the app takes the lowest. The bridge refuses any order that would pay a relayer more than 10% of what arrives, or the lower cap its admin sets.
  • Your limit. You set a slippage, 0.5% unless you change it and 10% at most. If the best relayer price is above it, no fast transfer is offered, and the bridge refuses an order that would pay the recipient less.

One transfer of 1,000 tokens, three ways, with a relayer charging 0.1% as the example price:

Who getsStandardFast, filledFast, unfilled
Platform23.9963.996
Relayer00.9960040
Recipient998995.007996996.004
Arrives in4 to 16 secondsseconds4 to 16 seconds

On the fast lane the fast bridge keeps 2 tokens and the token keeps 1.996, so 996.004 are on their way. The relayer pays the recipient 995.007996 at once and receives the 996.004 when they arrive, keeping 0.996004. Both platform fees are taken on the chain the transfer leaves, when it is sent, so a fast transfer that nobody fills has still paid them.

Becoming a relayer

Anyone can be a relayer. There is nothing to apply for and nothing to stake: you publish a price, watch for orders and fill the ones you want. Filling is never an obligation, and leaving an order unfilled takes nothing from anyone. It does count against you if the order was posted at your published price, as described under benching below.

What follows is the whole protocol, two HTTP calls and a handful of contract calls, and any program that follows it is a relayer. The HTTP calls are for the price board only: watching and filling work entirely on chain.

1. What you need

  • The token, on every chain you deliver to. Fills are paid from your own wallet. The fast bridge holds no stock of its own.
  • An approval. On each of those chains, approve that chain’s fast bridge to move your tokens: approve(fastBridge, amount) on the token. Without it your prices are not taken and a fill reverts.
  • Gas. A fill is a transaction on the destination chain, paid in its native asset: ETH on Ethereum, Base and Robinhood Chain, BNB on BSC.
  • An RPC endpoint for each chain. You read OrderPosted logs on the chains orders leave and send fills on the chains they go to, so the endpoint must serve eth_getLogs. A WebSocket endpoint makes you faster: subscribed to the fast bridge’s logs it tells you about an order the moment it is posted.
  • One key. The address that signs your prices is the address the app checks for tokens and approval, so sign with the wallet you fill from.

Check a bridge before you approve it. tokenOf(launchId) returns the token it moves for a launch, read from Pangea’s factories (isFactory(factory) says which it accepts), and remoteBridge(eid) its counterpart on another chain.

2. Publish your price

You send a signed price sheet to the price board. The board is public: read it to see the prices you are competing with, the ceiling, and who is benched. A route is a pair of chains on one shared bridge, so one price covers every token on that route; the app only takes it for a transfer when you hold that token on the destination.

Publish, POSThttps://www.pangea.trade/api/bridge/relayer-quotes
Read, GEThttps://www.pangea.trade/api/bridge/relayer-quotes
The body you POST
{
  "sheet": {
    "v": 1,
    "relayer": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "issuedAt": 1760000000,
    "expiresAt": 1760000120,
    "routes": [
      {
        "fromEid": 30184,
        "toEid": 30102,
        "bridge": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
        "feeBps": 10,
        "minFee": "1000000",
        "maxAmount": "50000000000000"
      },
      {
        "fromEid": 30101,
        "toEid": 30102,
        "bridge": "0xcccccccccccccccccccccccccccccccccccccccc",
        "feeBps": 10,
        "minFee": "1000000",
        "maxAmount": "50000000000000"
      }
    ]
  },
  "signature": "0x<65-byte signature, 130 hex characters>"
}
FieldWhat it is
vAlways 1.
relayerYour address. The signature must be this address’s.
issuedAt, expiresAtUnix seconds. A sheet may live at most 300 seconds, and its issue time may be at most 120 seconds ahead of the server’s clock.
routesOne to 64 routes. The same route may not appear twice.
fromEid, toEidThe LayerZero endpoint ids of the chain an order leaves and the chain you deliver on.
bridgeThe fast bridge on the chain the order leaves. Not the one you fill on.
feeBpsYour price in basis points of what arrives, a whole number from 0 to the board’s ceiling. The ceiling is 1000, which is 10%, unless the board reports another.
minFeeThe least you charge whatever the size, in base units, as a decimal string. Gas does not shrink with the amount.
maxAmountThe most you will front on that route, in base units, as a decimal string above zero.
solanaOptional, and needed for any route that delivers to Solana: your Solana address. The body then also carries solanaSignature, your Solana key’s Ed25519 signature over the same text, in base58 or base64. On a route from Solana, bridge is 32 bytes, written as 0x and 64 hex digits.
launchesOptional: up to 512 lines of toEid, launchId, minFee and maxAmount, to price each launch on its own -- every launch shares one fast bridge, and a route's amounts are base units of whichever token is sent. A destination with any launch line is quoted only for the launches it lists, at the route's feeBps and the line's minimum and limit (never above the route's own). Each is signed as launch <toEid> <launchId> min <minFee> max <maxAmount>, after the routes.

The signature is over the text below, signed as an Ethereum personal message: EIP-191, which wallets offer as personal_sign. The lines are joined by a single newline with none at the end, addresses are in lower case, and the routes are sorted by fromEid, then toEid, then bridge, whatever order the JSON lists them in. This is the text for the body above:

The text you sign
Pangea relayer quotes v1
relayer: 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
issued: 1760000000
expires: 1760000120
30101>30102 0xcccccccccccccccccccccccccccccccccccccccc 10bps min 1000000 max 50000000000000
30184>30102 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 10bps min 1000000 max 50000000000000

Your fee for a transfer is feeBps of what arrives, rounded up, and never less than minFee. If that comes to more than the ceiling allows on that transfer, or the transfer is larger than maxAmount, you have no price for it.

A sheet stops counting at its expiry, so publish again before then. Every 30 seconds is plenty, and the board accepts 120 publishes per caller in ten minutes. Each new sheet replaces your last one and must carry a later issuedAt. Stop publishing and you are off the board when your last sheet expires.

AnswerWhat it means
200Kept. The body is { ok, relayer, expiresAt }.
400The sheet is malformed, expired or over the ceiling. The reasons are in error.
401The signature is not this relayer’s over this sheet.
403This address may not publish on this deployment. The board is open to any address unless the deployment lists who may publish or has turned an address away.
409A sheet issued at the same time or later is already held for this relayer.
413The body is larger than 131,072 bytes.
429Too many requests. The message says how many seconds to wait.
424The board cannot be read or written right now.
What a GET returns
{
  "now": 1760000030,
  "maxFeeBps": 1000,
  "sheets": [
    {
      "sheet": "<a sheet, as published>",
      "signature": "0x<its signature>"
    }
  ],
  "benched": [
    {
      "relayer": "0x<address>",
      "until": 1760003600
    }
  ],
  "neverBenched": [
    {
      "relayer": "0x<address>",
      "strikes": 1
    }
  ]
}

Reading is limited to 300 requests per caller in ten minutes. maxFeeBps is the ceiling in force, sheets every live sheet with its signature, and benched the relayers whose prices are not being taken, with the unix second each bench ends. neverBenched lists the relayers this deployment never benches that have strikes within the hour, with how many.

How the app chooses. For each transfer it works out every live sheet’s price and looks at the 4 lowest, lowest first. Between equal prices the sheet with the earlier issuedAt comes first. It takes the first whose relayer holds what the fill pays, in that launch’s token on the destination chain, and has approved the fast bridge there for it. Benched relayers are left out. If that price would leave the recipient less than the sender’s slippage allows, no fast transfer is offered. When no relayer quotes a transfer at all, the fast lane is simply not shown.

3. Watch for orders

Every fast transfer is an order, posted on the fast bridge of the chain it leaves. That bridge emits one event for it:

On the source chain’s fast bridge
event OrderPosted(
    bytes32 indexed orderId,
    bytes32 indexed launchId,
    uint32 dstEid,
    bytes32 indexed sender,
    uint64 senderNonce,
    bytes32 to,
    uint256 amount,
    uint256 relayerFee,
    uint64 lzNonce
);
topic00x7e20a23f868a6866f04de222fcf771b4747be0d748f6c3af50083f019a56c231
FieldWhat it is
orderIdkeccak256(abi.encode(uint32 srcEid, bytes32 srcBridge, bytes32 launchId, bytes32 sender, uint64 senderNonce, uint32 dstEid, bytes32 to, uint256 amount, uint256 relayerFee)).
launchIdThe launch the order is for. The token on either chain is tokenOf(launchId) on that chain’s bridge.
dstEidThe endpoint id of the chain to deliver on.
sender, senderNonceWho posted the order, as 32 bytes, and that sender’s own counter on that bridge.
toThe recipient, as 32 bytes: an EVM address left-padded with zeros, or a Solana owner’s public key. An EVM destination refuses a recipient whose top 12 bytes are not zero.
amountWhat arrives on the destination, in base units. Not what the sender parted with: both platform fees are already out of it.
relayerFeeWhat the filler keeps, out of amount, in base units.
lzNonceThe LayerZero nonce of the message carrying the order. A fill has to name it.

Orders from Solana

An order from Solana is posted by Pangea’s fast-bridge program (post_order) and carried by the sender’s own OFT send, in the same Solana transaction. On an EVM destination, for source endpoint 30168, the order’s srcBridge is the sender’s own key (the OFT’s composeFrom), and the sender in the order is always taken to be that key: nobody can post in another’s name.

Verify before you fill. Skipping post_order only skips the fast lane’s fee, so an EVM-side check cannot tell you the order was paid for. Before filling an order from Solana, read the Solana transaction yourself and fill only when it holds both the post_order instruction and the OFT send it checks, with the same launch, recipient, amount and fee, and the transaction is finalized. A fill on anything less is a bet nobody else is making for you.

4. Fill

A fill is one call, made on the chain the order is going to:

On the destination chain’s fast bridge
function fillOrder(
    uint32 srcEid,
    bytes32 srcBridge,
    bytes32 launchId,
    bytes32 sender,
    uint64 senderNonce_,
    address to,
    uint256 amount,
    uint256 relayerFee,
    uint64 lzNonce
) external returns (bytes32 id);

srcEid is the endpoint id of the chain you saw the event on, and srcBridge the bridge that emitted it, as 32 bytes (the address left-padded with zeros): the destination accepts only a bridge it has named for that chain, else NotARemoteBridge. From Solana, srcBridge is the sender’s own key instead (see below). The other arguments are the event’s own fields, with to as the 20-byte address it pads: senderNonce_ is its senderNonce. In the same transaction the bridge moves amount - relayerFee of tokenOf(launchId) from your wallet to the recipient and records you as owed amount.

Every one of those fields is hashed into the order’s id. Change the source bridge, the launch, the amount, the recipient or the fee and you have filled an order nobody posted: you have paid someone, and nothing will ever repay you.

Check before you send, on the destination bridge. Each check is a read and saves the gas of a fill that would revert:

  • filledBy(orderId) is the zero address: nobody has filled it yet.
  • notYetArrived(token, srcEid, lzNonce), with token the launch’s token on this chain, is true: the order’s own message has not reached this chain. Once it has, there is nothing left to deliver early and a fill is refused.
  • You hold amount - relayerFee of the token there and your approval covers it.
  • relayerFee is at least your own price for that amount. You are never obliged to fill.
RevertWhat it means
NoRemote(srcEid)This bridge has no counterpart on that source chain.
NotARemoteBridge()The srcBridge you named is not a bridge this chain has ever named for that source chain.
UnknownLaunch(launchId)None of Pangea’s factories this bridge accepts knows that launch.
AlreadyFilled()Somebody filled it first. One fill per order.
AlreadySettled()The order has already been paid out.
FeeExceedsAmount()The fee is not less than the amount. No real order looks like this.
TooLateToFill()The message you named has already reached this chain.
A token transfer errorYour balance or your approval is too small.

A fill that goes through emits OrderFilled(orderId, relayer, to, paid).

5. Get repaid

You are repaid on the chain you filled on, in the token, when the order’s own message arrives and the bridge settles it: seconds after the order was posted. The bridge checks that the message came from the launch’s own token and the source chain’s own fast bridge before it pays anyone. settlement(orderId) on the destination bridge says how the order settled:

ValueWhat happened
0Not settled yet.
1Nobody filled it. The recipient was paid everything that arrived, the relayer’s fee included. Event: SettledSlow.
2A fill earned the fee. The filler was sent the whole amount. Event: RelayerPaid.
3A fill did not earn the fee. The filler was sent back amount - relayerFee, what it paid out, and the fee went to the recipient. Events: RelayerPaid and FeeReturned.
4Held. The recipient could not be paid on this chain (it is not an address here), so nobody could fill it and the bridge keeps it for the sender. Event: Refundable(orderId, guid, srcEid, sender, amount). It is never a strike against a relayer.

The same order can only arrive twice from Solana, when a sender reuses a nonce. The second arrival is paid to the recipient (ArrivedAgain and SettledSlow) and the first settlement stands: each arrival brought its own tokens, so none is refused.

Sending a held order back

Anyone may send a held amount back to its sender on the chain it came from: quoteRefund(guid) gives LayerZero’s fee for the message back, and refund(guid), paying that fee, sends it (Refunded). refunds(guid) shows what is held. The bridge screen offers this button itself when a transfer it sent is held.

What earns the fee. The fee pays for delivering early with your own tokens, so a fill earns it only when both of these are true:

  • The lzNonce your fill named is the nonce of the message that settles the order.
  • The settlement lands in a later block than your fill. A fill settled in its own block is repaid what it paid out and earns nothing.

filledBy(orderId) and filledLzNonce(orderId) return the filler and the nonce it named. So fill as soon as you are willing to.

Ceilings

LimitWhat it bounds
MAX_FAST_FEE_BPSThe fast lane’s own fee can never be set above 100 basis points, 1%. Read the fees in force for a launch with feesOf(launchId).
maxRelayerFeeBpsNo order may pay a relayer more than this share of what arrives: 1000 basis points, 10%, unless the bridge’s admin sets a lower one, for all launches or one. An order above it is refused with RelayerFeeTooHigh.
maxFeeBpsThe price board’s own ceiling on a published price, reported by every read of the board.
minReceivedThe sender’s floor, written into the order. An order that would pay the recipient less is refused with ReceivesTooLittle before any token moves.

What gets you benched

When the app sends an order at your published price, it remembers that the price was yours. Each order is judged once: reporting the same transaction again records nothing. It looks at the order again from 2 minutes after it was posted and judges it once it has settled, or after 30 minutes if it still has not. An order nobody filled, or one you filled without earning the fee, is a strike against you.

After 3 strikes within 60 minutes your prices are not taken for 60 minutes, however low they are. Those are the defaults, and a deployment can set its own. Your sheets stay on the board, and benched in the board’s answer says until when.

A deployment can name relayers it never benches, in BRIDGE_RELAYERS_NEVER_BENCH: EVM addresses, or Solana accounts a sheet names and has signed with. These are its own relayers, so nobody can switch its fast lane off by posting orders at its price while its stock is busy. Their unfilled orders are still struck and shown under neverBenched, but the strikes never add up to a bench. When the setting is empty, every relayer can be benched.

  • It is not a strike if any relayer’s fill earned the fee: the sender was served.
  • It is not a strike if another address filled the order without earning the fee within 90 seconds of it being posted and before the block that settled it, because that fill paid the recipient early and shut you out. A fill made later, or in the settling block itself, delivered nothing early, so it does not excuse you.
  • It is not a strike if you could not have filled it. When the app gives your price it reads, at one block of the destination chain, your balance of that launch’s token and your approval of the fast bridge, and it only gives your price if they cover the fill. What you could pay out is the lower of the two. An order is matched to the last quote given before it was posted, if that was no more than a few minutes earlier (an order posted long after its quote counts against nobody), so asking for the quote again later changes nothing. Only when a strike is about to be given, the app subtracts what you have paid out since in other fills of that same launch that served somebody, until the order settled (or until it is judged, if it never did), or 90 seconds after it was posted if that is later. If what is left is less than the order pays, there is no strike. So somebody draining your stock with many small orders first does not turn the larger ones after them into strikes. A fill that would excuse you but has not settled yet is waited for, up to 30 minutes after the order was posted.
  • A fill counts only if it was your own direct call of the bridge’s fillOrder for that launch (not through a contract or a batch, which cannot be read), came from an EVM chain, paid someone other than you, and earned the fee: it named the order’s own message and came before the block that settled it. A fill of an order nobody posted never settles, and one that names the wrong message or lands in the settling block is repaid at once, so neither excuses anything. Fills of orders from Solana do not count, because a plain token send from Solana can make one without paying the fast lane’s fee. Moving your stock away or revoking the approval excuses nothing either, nor does running out of native gas: both are yours to top up. Repayments that arrive in the meantime are not added back.
  • Into Solana only the balance read when the price was given is used, because there is no cheap list of your fills there. Orders into Solana are not judged yet.
  • If a read fails, there is no strike then. The order is checked again later, a few times, and if it still cannot be checked it is dropped without a strike, and the server logs it. A rate limit or a slow node is such a failure. Having more fills in the window than are read, or more in one block than the chain will list, is not: it is no excuse, and neither is a drain made of more than that many fills. The app makes at most a few of these checks a minute, and fewer for any one relayer, so a strike can come a little later than the order was due.
  • A fill does not clear earlier strikes. Each one stops counting 60 minutes after it was given.

The risks you take

Relaying is a business you run with your own money. Nothing here promises that it pays.

  • A reorg on the source chain. Between your fill and your repayment, the fill is unsecured. If the source chain reorganises and the order’s transaction is gone, the message never arrives and nothing repays you. How many blocks you wait before filling is your own trade between speed and that risk. If a reorg keeps the order but moves its message to a different LayerZero nonce, you are repaid what you paid out and earn nothing.
  • Filling before the order is in a block. Acting on pending transactions is the fastest way to fill and the riskiest: a transaction that has not been mined is likelier to vanish than a confirmed one.
  • Inventory. You hold the token on every chain you serve, and its price moves while you hold it. Your fee is paid in the token too.
  • Gas. A fill that reverts still costs gas, and gas is paid in the chain’s native asset while your fee is paid in the token. A price that does not cover gas loses money slowly.
  • Time. Your tokens are out until the order settles. Settlement is run by LayerZero’s executor; if it stalls, anyone may run it, and until someone does you wait.
  • Being shut out. Only one fill per order is accepted. Another address can fill ahead of you, even with a nonce that earns it nothing, and that order is then closed to you.

Contract addresses

One fast bridge per chain, shared by every launch: Ethereum, Base, Robinhood Chain and BSC (deployed 16 October 2026), then Solana. The full interface is the bridge’s ABI and INTERFACE.md. These are the addresses, and the endpoint ids a price sheet and a fill use:

ChainFast bridgeEndpoint idChain id
Ethereum0xE6feCfb8520Ce9417beB569ef95d6b8304802897301011
Base0x51D658a71AEa38f123d4c73e42703Fb9C949900A301848453
Robinhood Chain0xcC87BeDb95E4ac0cf20039442A964ADB390c02c4304164663
BSC0xd7C10226327451ac768512aC4A5035a9477f862a3010256
G.13

For integrators and AI agents

G.13

Pangea is a token launchpad. Each launch deploys its token as five contracts, a LayerZero OFT on Ethereum, Base, Robinhood Chain and BSC and a mint on Solana, and opens one liquidity pool per chain at the same US dollar price. This section is the reference for reading and using it from code.

These docs are also published as Markdown, generated from the same source as this page:

Index for language modelshttps://www.pangea.trade/llms.txt
Everything in one filehttps://www.pangea.trade/llms-full.txt
These docs as Markdownhttps://www.pangea.trade/app/docs.md

Each section is also on its own, at /app/docs/<section id>.md. This section is /app/docs/integrators.md.

The five chains

ChainName in the APIChain idEndpoint id
Ethereumethereum130101
Basebase845330184
Robinhood Chainrobinhood466330416
BSCbsc5630102
Solanasolananone30168

The endpoint id is LayerZero’s, and it is how the token names a chain. Solana has no EVM chain id.

Units

  • Every token has 9 decimals, on all five chains. An amount in base units is a whole number at 9 decimals: one token is 1,000,000,000 base units.
  • A transfer between chains crosses at 6 decimals. It is rounded down to a multiple of 1,000 base units, and the odd base units stay in the sender’s wallet.
  • Fees are in basis points. One basis point is 0.01%.
  • The standard supply is 5 billion tokens, 1 billion in each chain’s liquidity pool at launch. On an EVM chain totalSupply() starts at that chain’s 1 billion and is a reported figure that moves no balance, so every chain shows a market cap on the same basis (LayerZero routes says how it is reported); launchTotal() is the sum across the launch’s chains, and localSupply() what is on that chain now. On Solana the mint’s supply is what is on Solana now: tokens bridged in are minted there and tokens bridged out are burned. The market cap situation explains why terminals show different market caps.
  • A launch on selected chains is 1B on each of its chains only. A degen mode launch is 1B in all: each chain mints an equal share, every chain’s totalSupply() and launchTotal() report the 1B, and on Solana the mint holds the full 1B with the rest in its bridge reserve. Degen mode has the figures.
  • Times in the API are unix seconds unless a field says otherwise.

Rules that surprise people

  • Every launch is paid for by the person launching it, once, on the chain they pick, in that chain’s native asset. There are no free launches.
  • Every launch opens at the same price on every chain, at the market cap its creator chose for the whole coin, every chain together: $3,000 to $500,000 a chain times the chains (in all, for a degen mode coin), signed into the launch. The price per token is that market cap over the whole supply. Launches before factory v2 all opened at $3,000 a chain, $0.000003 a token. Read a token’s price from the API; do not assume its opening one. On factory v3 there is no locked supply: degen mode is normal mode, every chain minting its own 1B and pooling all of it, opening at $600 a chain ($3,000 for a coin on five chains). The least a v3 launch may open at is $600 a chain, which the platform may set between $600 and $3,000 a chain; standard mode still opens at $3,000 a chain unless the creator picks another cap.
  • A token has one address per chain it is on, all different (or one address on the EVM chains, for a token launched with “Same contract address on all EVM chains”), and every one of them ends in 6ea: the last three hex digits on the EVM chains, the last three characters of the mint on Solana. Read the addresses from the API. Do not assume two chains share one.
  • A transfer between chains of a token from before factory v3 costs 0.2% of the amount, kept by the token for the platform, plus LayerZero’s message fee in the source chain’s native asset. A factory v3 token's bridge fee is its chain's registry figure (0 to 0.5%), and a transfer from the chain where it is cheaper to the chain where it is dearer also pays the price-gap fee, burned at the destination: the quote's gap.burned, an estimate. It is attested after 1 confirmation on its source chain and typically arrives in 4 to 16 seconds.
  • Each launch sets its trading fee once, between 0.3% and 5%. No contract function or program instruction changes it afterwards.
  • The first transfer on a Solana route nobody has used yet also pays a one-time setup fee for that route. Every later transfer on that route does not. Any wallet can pay it, a smart-contract wallet too, except a Safe: its transactions wait for its owners, so a Safe bridges once the route is set up. Pay it from your own wallet, as the bridge page does: a payment sent through another contract (a multicall or a router) is refunded to that contract, not to you, and one made by delegatecall is not seen at all.
  • On the four EVM chains nothing can mint more of a token. On Solana the mint authority is the token's store in Pangea's deployment of LayerZero's OFT program, which mints exactly what was burned on another chain. Bridging burns on one chain exactly what it mints on another, with one exception: a factory v3 token's price-gap fee is withheld at the destination and never minted, so it is burned.
  • Prices on the five chains are separate markets. They are held together by arbitrage, not by a mechanism, and can differ.

What you can and cannot do from code

  • You can read every launch’s pools, prices, reserves and addresses, its trades and volume, recent bridge transfers and its metadata, over the HTTP endpoints below.
  • You can trade directly against the pools: Uniswap v4 on the EVM chains, Meteora DAMM v2 on Solana. The API gives each pool’s id and venue.
  • You can bridge by calling the token itself, or price a transfer over HTTP and sign the transaction it returns.
  • You can launch over the Launch API: quote a launch, pay for it from your own wallet, hand it over and follow it. The price snapshot every chain launches with is still signed by the platform, after your payment is checked. Launch API →
  • No API key is needed and there are no accounts. Every endpoint here is public. Where one is limited, the limit is per caller, by IP address. The Launch API has no rate limit for normal use, and takes an optional partner key: API keys and limits →.
  • Do not crawl the API. Each read costs reads on five chains. Call an endpoint when you need its answer; robots.txt asks crawlers to stay out of /api/.

HTTP endpoints

All are under https://www.pangea.trade/api. They take and return JSON and need no key. Over a limit the answer is 429 with a message saying how many seconds to wait. An error is { "error": "..." }. A value the chains could not give is null rather than a guess, so check for it.

EndpointWhat it returnsLimit per caller
GET /poolsEvery launch, with its pool on each chain: addresses, price, reserves, supply.None. Every caller is served one shared reading: re-read as soon as a trade moves a pool while the live page is open, and at least every 60 seconds unless the deployment sets another interval.
GET /historyTrades, volume in US dollars and price candles.30 uncached reads in ten minutes. An answer is reused for 45 seconds.
GET /bridge/activityRecent transfers between chains.None.
GET /token/metaA token’s own metadata document: description, image, links.None.
POST /bridge/quoteA transfer between chains, priced, with the transaction to sign.120 in ten minutes and 15 in any one minute.
Every public endpoint, including the ones not described here, is in API reference →
Pools, GEThttps://www.pangea.trade/api/pools

No parameters. The answer is { live, readAt, rates, blockers, launches }. When nothing has launched, live is false and launches is empty. Each launch carries:

FieldWhat it is
slug, name, symbolThe launch’s name in every other endpoint (slug), and its name and symbol.
pools[]One entry per chain, including chains that could not be read.
pools[].chainThe chain’s name in the API.
pools[].tokenThe token’s address on that chain. This is where a token’s five addresses are published.
pools[].poolId, pools[].venueThe pool’s id and the venue it is on.
pools[].price, pools[].priceUsdQuote asset per token, and the same in US dollars. quoteSymbol names the quote asset.
pools[].reserves, pools[].tokenReservesWhat the pool holds: quote asset, and whole tokens.
pools[].localSupplyWhole tokens on that chain now.
pools[].errorWhy that chain has no reading, when it has none. Set only when there is no price.
pools[].note{ kind, text }, when present: a fact about a pool whose price was read and is good, never an outage. kind is "extra-liquidity" (others added liquidity to the pool; reserves are the launch’s position only), "range-unknown", "position-unknown" or "position-mismatch" (the reserves could not be worked out).
launchTotal, launchTotalRawThe token’s total supply in whole tokens, and in base units as a string. For a perChain launch, 1B on each chain it is on; for a degen mode launch, 1B.
supplyModel, capSupply"perChain" for a launch with 1B on each chain (1B per chain · 5B across 5 chains), "global" for a degen mode launch and for launches before 2026-10-15, "per-chain" for a v4 coin (1B per opened chain; capSupply the supply across its open chains, capSupplyMax 1B times its listed chains). Market cap is a price times capSupply, which is launchTotal: the whole supply across the launch’s chains (5B for perChain on five). For a degen mode coin it is circulating: the 1B less what was burned; circulating is null for any other coin.
degentrue for a coin launched in degen mode: 1B in all, every chain reporting the 1B.
launchChains, chainsTotalThe chains the launch is on, and how many: five unless it was launched on selected chains.
cheapest, spreadUsdThe chain with the lowest US dollar price, and the gap between the highest and lowest price as a fraction.
sold, burntWhole tokens that have left the pools, and whole tokens sent to the burn addresses across chains: 0x…dEaD on each EVM chain, the incinerator's token account on Solana.
metadataURIThe token’s metadata document, as the contract names it.
History, GEThttps://www.pangea.trade/api/history

Query: launch, a slug, or leave it out for every launch; hours, a whole number from 1 to 168, 24 unless given. The answer is { readAt, hours, cursor, rates, totals, launches }. An unknown slug is a 404. Each launch carries volumeUsd, trades, coverage, chains, candles and recent.

  • coverage runs from 0 to 1. Below 1 the figures describe part of the window you asked for, not all of it. Show it, or check it.
  • candles are { t, o, h, l, c, v, vu }, one per bucket that traded, where t is the bucket's start. A bucket with no trade is left out, not filled with the last price. The prices are in US dollars. v is the quote volume in each chain's own asset, so across chains it adds ether, BNB and SOL together; vu is the bucket's volume in US dollars at the same rates as volumeUsd, and null when a trade in it had no rate.
  • recent is the last trades, newest first: { id, chain, timestamp, txHash, side, tokenAmount, quoteAmount, quoteSymbol, priceUsd, valueUsd }, where side is buy or sell.
  • A coin's whole history, a page at a time: add trades=latest (the newest page) or trades=before:<next> (the page after the one that gave next) with launch, and limit of 50, 100 or 200. The answer is { slug, readAt, trades, next }, every chain newest first, the rows as in recent; next is null on the page that reached the first trade. The first page also carries total, every trade since the launch, and complete, whether every chain is recorded back to it.
Bridge activity, GEThttps://www.pangea.trade/api/bridge/activity

Query: token, a slug, optional; hours, 1 to 168, 24 unless given; limit, 1 to 100, 25 unless given. The answer is { recording, hours, since, uncovered, totals, cursor, transfers }. A transfer is { id, guid, slug, from, to, at, amount, party, complete, tookSeconds }.

  • recording false means this deployment keeps no record, and the list is empty for that reason, not because nobody bridged. reason says why.
  • uncovered lists the chains whose sends are not recorded. A transfer out of such a chain appears as an arrival with no send.
  • complete false means one side is recorded: a transfer in flight, or one that failed. The feed does not guess which.
Token metadata, GEThttps://www.pangea.trade/api/token/meta

Query: launch, a slug, required. The answer is { uri, resolvable, name, symbol, description, image, website, twitter, telegram }, or { uri, resolvable: false, reason } when the document cannot be opened. Every field is the token author’s own claim. The name and symbol that cannot be edited are the contract’s, in the pools endpoint.

Bridge quote, POSThttps://www.pangea.trade/api/bridge/quote
The body you POST
{
  "slug": "<launch slug>",
  "from": "base",
  "to": "bsc",
  "amount": "1000000000000",
  "user": "0x<sender, and the recipient unless one is given>"
}
FieldWhat it is
slugThe launch.
from, toChain names, as in the table of chains above. They must differ.
amountBase units, as a decimal string.
userThe EVM address sending, and the recipient on an EVM chain unless one is given.
solanaUserThe Solana address, when Solana is the source or the destination.
recipientWho receives, when it is not the sender. Required for a transfer between an EVM chain and Solana: across the two a transfer is never sent to a wallet by default.

The answer is { symbol, from, to, amount, routes }, where routes holds two routes: canonical, the token’s own LayerZero transfer, and fast, the same transfer with a fast order. A route that cannot be used says why in unavailable. It carries:

FieldWhat it is
idcanonical or fast. The fast route’s own fields are in API reference.
amountOutWhat the transfer credits before a factory v3 token's price-gap fee, in base units. For a token from before factory v3 that is what the recipient receives. For a factory v3 token what arrives is amountOut less gap.burned (an estimate, in gap.decimals; every v3 token has 9 decimals on every chain) and, into Solana, less gap.delivery.amount too (our delivery key's cost there), and never less than gap.minCredited: below it the arrival waits at the destination. Never build your own minimum from amountOut alone.
gapA factory v3 token only: the price-gap estimate for this transfer. state is estimate, free (nothing withheld), held (the destination cannot price it yet: it waits there) or unavailable (no standard route now; ask again); burned what the destination withholds, minCredited the least it may credit (gapToleranceBps under the estimate), and minCreditedSource that minimum in the source's decimals, out of Solana.
nativeFeeWhat it costs in the source chain’s native asset, in its smallest unit. Send it as the transaction’s value.
tokenFee, platformFeeEverything taken in the token, and the platform’s part of it, in base units. Both are the token’s fee.
secondsThe estimated time.
callThe transaction to sign: { to, data, value, chainId }. Null when the transfer cannot be built as one contract call, as out of Solana.
setupFeePresent when this transfer opens a Solana route: the part of nativeFee that pays for it, once.
unavailablePresent when the route cannot be used, with the reason.

Bridging from code

On an EVM chain the token is a LayerZero OFT and you call it directly. Ask quoteOFT what a transfer debits and delivers, name the delivered amount as the minimum, ask quoteSend for the message fee, and send that fee as the transaction’s value. Leave extraOptions empty: the gas the destination needs is already enforced by the token.

On the token, on the chain the transfer leaves
struct SendParam {
    uint32 dstEid;       // the destination's endpoint id
    bytes32 to;          // the recipient, left-padded to 32 bytes
    uint256 amountLD;    // base units
    uint256 minAmountLD; // the least that may arrive
    bytes extraOptions;
    bytes composeMsg;
    bytes oftCmd;
}
struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }
struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }

function quoteOFT(SendParam calldata _sendParam)
    external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken)
    external view returns (MessagingFee memory);
function send(SendParam calldata _sendParam, MessagingFee calldata _fee, address _refundAddress)
    external payable returns (MessagingReceipt memory, OFTReceipt memory);

event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD);
event OFTReceived(bytes32 indexed guid, uint32 srcEid, address indexed toAddress, uint256 amountReceivedLD);

error AmountTooSmallToBridge(uint256 amount);
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
Read on the tokenWhat it returns
BRIDGE_FEE_BPS()A token from before factory v3: its bridge fee in basis points, 20. A factory v3 token's fee is its chain's registry figure (0 to 50): take what arrives from quoteOFT.
bridgeFeeTo()Where that fee goes on this chain.
peers(uint32 eid)The token’s counterpart on another chain. Zero means there is no route to it.
decimals()9, on every chain.
totalSupply()The reported supply: this chain’s 1B slice, or, for a launch with a SupplyReporter, the token’s supply on Solana as the chain’s SupplyReporter mirrors it (on a factory v1 token, never below localSupply() or above launchTotal()). For a degen mode launch, the 1B total, which never changes.
launchTotal(), launchSlice()The whole launch’s supply across every chain, and this chain’s slice of it.
supplyReporter()The only address that may report this token’s supply. Launches made before reporting existed have no such function.
localSupply()What is on this chain now.
metadataURI()The token’s metadata document.

On Solana the token is an account of LayerZero’s OFT program, SNyKyiZYU4R54SF9sYLmjCaCUsa1sC6QouNAj3x3Zzf, and a transfer out of Solana is an instruction to that program, not a contract call. Into Solana the recipient is the wallet that will own the tokens.

Launches on chain

On each EVM chain the launch factory emits one event per launch. The current factories’ addresses are in LayerZero routes.

On each EVM chain’s launch factory
event Launched(
    bytes32 indexed launchId,
    address indexed token,
    address splitter,
    address launchOwner,
    address locker,
    uint128 liquidity,
    uint256 dust
);

Addresses

  • A token’s five addresses are in the pools endpoint, at launches[].pools[].token, and on the token’s page.
  • The current launch factories, and each chain’s LayerZero endpoint, libraries, executor and verifiers, are in LayerZero routes.
G.14

Bridging from code

G.14

On the four EVM chains you call the token itself: it is a LayerZero OFT. Out of Solana the send is an instruction to LayerZero’s OFT program, and the simplest way to build it is to ask Pangea’s API for the unsigned transaction. The signatures and events are in For integrators; the routes they travel are in LayerZero routes.

  • amountLD is in base units, nine decimals. The token keeps 0.2% of it (a factory v3 token: its chain's registry figure, at most 0.5%), rounded down, and what crosses is rounded down to six decimals. quoteOFT returns both figures, exactly.
  • minAmountLD is the least that may arrive, or the send reverts with SlippageExceeded. On a token from before factory v3 the fee is fixed, so the received amount from quoteOFT can be used as it is; on a factory v3 token the fee is read at every send, so ask quoteOFT right before you send.
  • extraOptions stays empty. The token adds its own gas floor to every send; anything you add is added on top and paid for by you.
  • A factory v3 token: send the minimum. Its oftCmd must carry your least credited after the price-gap fee: abi.encode(uint256 minCreditedLD) in the source's base units (the low 248 bits; the top byte is flags, 0). With an empty oftCmd the destination credits whatever the gap leaves. Take it from the quote's gap object (gap.minCredited, destination units; gap.minCreditedSource out of Solana), set by gapToleranceBps (default 100, at most 2000). Its bridge fee is its chain's registry figure, 0 to 0.5%: read what arrives from quoteOFT.
  • to is 32 bytes: an EVM address left-padded with zeros, or a Solana wallet’s public key. Never a Solana token account.
  • quoteSend gives LayerZero’s fee for that exact SendParam. Send it as the transaction’s value, with payInLzToken false. Any excess goes back to the refund address.
  • No approval is needed for a standard send: the token burns from your own balance.

EVM to EVM, with viem

Quote a transfer of 1,000 PTEST4 from Base to BSC
import { createPublicClient, http, parseAbi, pad } from "viem";
import { base } from "viem/chains";

const OFT = parseAbi([
  "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
  "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
  "struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
  "struct OFTFeeDetail { int256 feeAmountLD; string description; }",
  "struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }",
  "struct MessagingReceipt { bytes32 guid; uint64 nonce; MessagingFee fee; }",
  "function quoteOFT(SendParam _sendParam) view returns (OFTLimit, OFTFeeDetail[], OFTReceipt)",
  "function quoteSend(SendParam _sendParam, bool _payInLzToken) view returns (MessagingFee)",
  "function send(SendParam _sendParam, MessagingFee _fee, address _refundAddress) payable returns (MessagingReceipt, OFTReceipt)",
]);

const TOKEN = "0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea"; // PTEST4 on Base
const BSC = 30102;
const recipient = "0x000000000000000000000000000000000000bEEF"; // who receives on BSC
const amount = 1_000n * 10n ** 9n; // 1,000 tokens: nine decimals

const client = createPublicClient({ chain: base, transport: http(process.env.BASE_RPC) });

// What the transfer debits and what arrives: 0.2% stays with the token, the rest crosses.
const sendParam = {
  dstEid: BSC,
  to: pad(recipient), // an EVM address, left-padded to 32 bytes
  amountLD: amount,
  minAmountLD: 0n,
  extraOptions: "0x", // empty: the token already enforces the gas the destination needs
  composeMsg: "0x",
  oftCmd: "0x",
};
const [, , receipt] = await client.readContract({ address: TOKEN, abi: OFT, functionName: "quoteOFT", args: [sendParam] });
sendParam.minAmountLD = receipt.amountReceivedLD; // the least that may arrive

// LayerZero's fee for this exact transfer, in ETH.
const fee = await client.readContract({ address: TOKEN, abi: OFT, functionName: "quoteSend", args: [sendParam, false] });
console.log({ sent: receipt.amountSentLD, arrives: receipt.amountReceivedLD, nativeFee: fee.nativeFee });

// The send, from the holder's wallet, paying the fee as the value:
// await wallet.writeContract({ address: TOKEN, abi: OFT, functionName: "send",
//   args: [sendParam, fee, account.address], value: fee.nativeFee });

Run against Base on 19 October 2026, it printed sent: 1000000000000n, arrives: 998000000000n, nativeFee: 109546395808232n: 1,000 tokens sent, 998 arrive, and about 0.00011 ETH for LayerZero.

The same, with cast

quoteOFT and quoteSend from the command line
TOKEN=0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea
RPC=your-base-rpc-endpoint
TO=$(cast pad --len 32 0x000000000000000000000000000000000000bEEF)

# what is sent and what arrives: (limit), (fee details), (sent, received)
cast call $TOKEN "quoteOFT((uint32,bytes32,uint256,uint256,bytes,bytes,bytes))((uint256,uint256),(int256,string)[],(uint256,uint256))" \
  "(30102,$TO,1000000000000,0,0x,0x,0x)" --rpc-url $RPC

# LayerZero's fee in wei: (nativeFee, lzTokenFee)
cast call $TOKEN "quoteSend((uint32,bytes32,uint256,uint256,bytes,bytes,bytes),bool)((uint256,uint256))" \
  "(30102,$TO,1000000000000,998000000000,0x,0x,0x)" false --rpc-url $RPC
Pad an address with cast pad, which pads on the left. cast to-bytes32 pads on the right, and a send to that would go to a different address.

EVM to Solana

The same send, with dstEid 30168 and the recipient’s Solana wallet as the 32 bytes. The tokens arrive in that wallet’s token account, created on delivery; its rent, 2,074,080 lamports, is in every transfer’s gas floor and so in quoteSend’s fee.

Quote a transfer from Base to Solana, and check the route is open
import { createPublicClient, http, parseAbi, toHex } from "viem";
import { base } from "viem/chains";
import { Connection, PublicKey } from "@solana/web3.js";

const OFT = parseAbi([
  "struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; }",
  "struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; }",
  "struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; }",
  "struct OFTFeeDetail { int256 feeAmountLD; string description; }",
  "struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; }",
  "function quoteOFT(SendParam _sendParam) view returns (OFTLimit, OFTFeeDetail[], OFTReceipt)",
  "function quoteSend(SendParam _sendParam, bool _payInLzToken) view returns (MessagingFee)",
  "function peers(uint32 eid) view returns (bytes32)",
]);

const TOKEN = "0x2dEC1F0FBe66ef78a7cec00cD1FD9c58345ED6Ea"; // PTEST4 on Base
const BASE_EID = 30184;
const SOLANA = 30168;
const owner = new PublicKey("<the Solana wallet that will own the tokens>");
const amount = 1_000n * 10n ** 9n;

const evm = createPublicClient({ chain: base, transport: http(process.env.BASE_RPC) });
const solana = new Connection(process.env.SOLANA_RPC, "confirmed");

// Is this token's route from Base open on Solana? It is once LayerZero's receive config exists.
const peer = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "peers", args: [SOLANA] });
const store = new PublicKey(Buffer.from(peer.slice(2), "hex")); // the OFT store
const eid = Buffer.alloc(4);
eid.writeUInt32BE(BASE_EID);
const ULN = new PublicKey("7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH");
const [receiveConfig] = PublicKey.findProgramAddressSync([Buffer.from("ReceiveConfig"), eid, store.toBuffer()], ULN);
const open = (await solana.getAccountInfo(receiveConfig)) !== null;

const sendParam = {
  dstEid: SOLANA,
  to: toHex(owner.toBytes()), // the owner's public key as 32 bytes, not a token account
  amountLD: amount,
  minAmountLD: 0n,
  extraOptions: "0x", // the token adds the gas floor and the rent of the token account
  composeMsg: "0x",
  oftCmd: "0x",
};
const [, , receipt] = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "quoteOFT", args: [sendParam] });
sendParam.minAmountLD = receipt.amountReceivedLD;
const fee = await evm.readContract({ address: TOKEN, abi: OFT, functionName: "quoteSend", args: [sendParam, false] });
console.log({ store: store.toBase58(), routeOpen: open, arrives: receipt.amountReceivedLD, nativeFee: fee.nativeFee });
// Then send exactly as from EVM to EVM. With routeOpen false, the transfer waits on Solana
// until the route is opened: see the route setup below.

If the token’s route from that chain is not open on Solana yet, a transfer sent now waits there until it is. To open it as part of your transfer, as the Bridge page does, POST /api/bridge/quote returns the setup payment in routePayment: send it first, then report it to POST /api/bridge/route-open, which opens the route or refunds the payment, and then send the transfer. API reference →

Solana to EVM, with @solana/web3.js

Out of Solana the send is LayerZero’s OFT program’s own instruction, with about thirty accounts and a lookup table. POST /api/bridge/solana-tx builds it, unsigned, for your wallet to sign: it names the accounts, sets the minimum to what arrives, and adds the route’s opening when a route is used for the first time. You pay LayerZero’s fee and the transaction fee in SOL; the token’s 0.2% is kept in the OFT store’s escrow for the platform.

Price and build a transfer from Solana to Base, then simulate it
import { Connection, VersionedTransaction } from "@solana/web3.js";

const API = "https://www.pangea.trade/api";
const sender = "<the Solana wallet holding the tokens>";
const recipient = "0x000000000000000000000000000000000000bEEF"; // who receives on Base
const amount = (1_000n * 10n ** 9n).toString(); // 1,000 tokens, in base units

const post = (path, body) =>
  fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
    .then(async (r) => ({ status: r.status, body: await r.json() }));

// The price: what arrives on Base, and LayerZero's fee in lamports.
const quote = await post("/bridge/quote", { slug: "ptest4", from: "solana", to: "base", amount, user: recipient, recipient, solanaUser: sender });
const standard = quote.body.routes?.find((r) => r.id === "canonical");
if (!standard || standard.unavailable) throw new Error(standard?.unavailable ?? quote.body.error);
// A factory v3 token: what arrives is amountOut less the gap estimate, never less than gap.minCredited.
const gap = standard.gap ?? null;
console.log({ credited: standard.amountOut, gapBurned: gap?.burned, nativeFee: standard.nativeFee, setupFee: standard.setupFee ?? null });

// The unsigned transaction: the OFT send, and the route's opening if it is not open yet.
// A factory v3 token's send must name its minimum (minCredited, Solana base units): without one it is refused (400).
const ask = { slug: "ptest4", to: "base", amount, sender, recipient, ...(gap ? { minCredited: gap.minCreditedSource } : {}) };
let built = await post("/bridge/solana-tx", ask);
// 202 { pending: true }: a price record or a route opening is still landing -- ask again in a moment.
while (built.status === 202) {
  await new Promise((r) => setTimeout(r, 2_000));
  built = await post("/bridge/solana-tx", ask);
}
// stage "record" (a v3 token with no recent price on record): sign and send it, then ask again with
// { ...ask, recorded: <its signature> }; the answer is then the send.
if (built.status !== 200) throw new Error(built.body.error ?? `stage ${built.body.stage}`);
const tx = VersionedTransaction.deserialize(Buffer.from(built.body.transaction, "base64"));

// Check it before signing: a simulation sends nothing.
const connection = new Connection(process.env.SOLANA_RPC, "confirmed");
const sim = await connection.simulateTransaction(tx, { sigVerify: false });
console.log({ stage: built.body.stage, received: built.body.received, totalFee: built.body.totalFee, error: sim.value.err });

// Sign with the sender's wallet and send:
// const signed = await wallet.signTransaction(tx);
// await connection.sendRawTransaction(signed.serialize());

Run read-only on 19 October 2026 from a Solana holder, it built a transaction at stage send with received: 998000000000 and a fee of at most 636,478 lamports, and the simulation passed. The stages are send, the transfer alone; together, the route’s opening and the transfer in one transaction; and setup, the opening alone, after which you ask again with after set to its signature. A factory v3 token adds record: the price record its send needs first (its signature fee only), after which you ask again with recorded set to its signature. A 202 with pending: true means the step before is still landing: ask again in a moment. A v3 token's send also needs minCredited (the quote's gap.minCreditedSource), or it is refused with a 400.

After the send

  • The send emits OFTSent(guid, ...) on the source chain and the delivery OFTReceived(guid, ...) on the destination. The guid is the transfer’s id on LayerZero Scan.
  • Look the guid or the transaction up on LayerZero Scan to follow it, or poll the destination for OFTReceived with that guid.
  • Delivery needs nothing from you. If a message sits verified and undelivered, anyone may deliver it, except a factory v3 token's charged arrival into Solana, which only Pangea's appointed deliverers may land.
G.15

API reference

G.15

All endpoints are under https://www.pangea.trade/api, take and return JSON, and need no key or account. The general rules are in For integrators, which also describes five of them in full: pools, history, bridge activity, token metadata and the bridge quote. The relayers’ price board is in Relayers. This section lists all of them and describes the rest.

  • An error is { "error": "..." } with a 4xx status (a 5xx is a fault, and its body may be a proxy's own). A value the chains could not give is null, not a guess.
  • Limits are per caller, by IP address, counted on each server. Over a limit the answer is 429 and the message says how many seconds to wait; some also send Retry-After.
  • A JSON body that is too large is 413, one that is compressed is 415, one that does not arrive within 10 seconds is 408, and one that is not a JSON object of the expected fields is 400.
  • Call them from a server or a script. Only the supply endpoints (/supply/[token] and its two plain-number forms), /gap-price/[id] and /gap-price/quote-usd send Access-Control-Allow-Origin: *; the rest send no CORS header, so a web page on another site cannot read them from the visitor’s browser.
  • Answers are cached briefly in front of the app (most for 5 to 60 seconds, as each endpoint says), so polling faster than that returns the same answer.
  • Amounts are base units as decimal strings. Chains are named ethereum, base, robinhood, bsc and solana. A launch is named by its slug.
  • Three endpoints answer as a stream of server-sent events: data: <json> lines, with content-type: text/event-stream. Read them with a streaming fetch; they are POST, so an EventSource cannot open them. The Launch API’s event stream and the launches stream are GETs, in the same format with named events.
  • A coin is named by its slug, its launch id or any chain’s token address wherever the path says [id]. The coin, its supply, the pools, the holders and the launches feed carry degen and the coin’s supply: circulating, the bridge reserve and where it is held, the total, and what each chain mints and each token reports.

Every public endpoint

EndpointWhat it is forLimit per caller
GET /poolsEvery launch and its pool on each chain.None; one shared reading.
GET /historyTrades, volume and price candles.30 in ten minutes, uncached reads only.
GET /holdersWho holds a token, per chain.60 in ten minutes, uncached reads only.
GET /gap-price/[id]A shared-bridge coin’s signed gap price for one transfer: the coin id or its token, src and dst (chain id or name, several dst comma-separated), and optionally amount (the send in the source token’s base units). Pass the answer unchanged in the bridge call. 503 means no signed price right now: send without one (the chain’s own readers price it, or no gap fee). Not cached.20 at once, then 1 a second; all callers 50 a second.
GET /gap-price/quote-usdThe signed USD price of a coin’s native quote on one EVM chain, for a buy-and-bridge that carries its own quote price (buyAndSendQuoted): chain (chain id or name) and optionally quote (0x00..00, the native coin, is the only one signed). Pass signedQuote unchanged in the call, and fetch it right before: it is valid for about 30 seconds. 503 means no price right now; on Robinhood a buy then waits, since that chain has no other quote price. 404 for an ERC-20 quote. Not cached.Shared with the gap price: 20 at once, then 1 a second; all callers 50 a second.
GET /token/metaA token’s metadata document.None.
GET, POST /token/socialsWho may edit a coin’s socials (its creator, EVM and Solana); with history=1 every version so far, each with the creator’s signature, and with onchain=1 each EVM chain’s metadata authority and URI; POST stores a new version the creator signed (EIP-712, or a signed message on Solana) with a new website, X, Telegram, Discord, description or logo. No transaction and no gas: /token/meta shows the newest version, marked as edited by the creator.GET 120 a minute, 10 with onchain=1. POST 30 attempts in ten minutes; 10 stored edits an hour, and 6 an hour and 24 a day per coin.
GET /logo/[cid]/[file]A token’s logo from this site, as square WebP: the file is 64.webp, 128.webp or 256.webp, and the CID is the image’s own, as /token/meta names it. Cached for a year: a CID’s picture cannot change.None.
GET /platform-fundsWhat is still to be spent on PANGEA buybacks on each chain: the buyback wallets’ balance less what they keep for gas, plus their share of platform fees not yet forwarded, in native units and US dollars, with the total and the buyback wallets.None; one shared reading a minute.
GET /creator-rewardsEverything launches’ trading fees have paid to their creators, all time: per chain in native units and US dollars at today’s prices, with the total and which chains are not counted in full.None; one shared reading a minute.
GET /holder-payoutsCreator fees paid to holders by the coins whose fees go to a tokenomics vault: all time (every vault the coin’s fees have gone to, earlier ones included) and the last 24 hours, by chain, by coin and by reward token, as each vault’s own counters on chain say, in US dollars at today’s prices.None; one shared reading a minute.
GET /revenuePlatform and creator fee payouts over any span (from, to), in US dollars at each payout’s own time, per chain and asset.None; held 30 seconds.
GET /buyback-burnsEvery PANGEA buyback so far, with its burn transaction: totals per chain, the latest 50, or every one a page at a time.None; held 15 seconds.
GET /ctoThe coins under a community takeover, and when each chain’s fee stream moved.120 a minute.
GET /stock-pairsThe coins with a stock-paired chain, and the stock on each.120 a minute.
GET, POST /token/fee-nativeWhether a coin’s creator fees in its pair token (any pair but the chain’s own coin) are paid in the chain’s native coin; POST switches it, signed by the creator.GET 120 a minute; POST 20 in ten minutes.
GET /tokenomicsOne chain’s tokenomics vault, read from the chain: its split, what waits in each bucket, and what it has burned, bought and added.60 a minute.
GET /tokenomics/v2/burnA tokenomics v2.2 vault’s burn tokens: the other tokens it buys to burn, and how much of each so far.30 a minute.
GET /tokenomics/v2/holderWhat one holder is owed and has been paid by a coin’s vault, per reward token.30 a minute.
POST /tokenomics/v4/switchWhere v4 is switched on: a v4 coin’s tokenomics on one chain, built for your own wallet to sign; nothing is signed or sent here. op: "enable" (with vault) turns it on or re-points it, "disable" turns it off, "clear" clears a wrongly pinned vault. The creator may only turn it on, while it is off and the role is not renounced; the platform admin may turn it on, off and re-point it at any time (EVM adminEnableTokenomics; Solana admin_enable_tokenomics, on or re-point). For a Solana-home coin, "push" builds the creator role push from Solana, "role" reads it back, and "authorize" builds the open authorisation from Solana (pusher, opener, deadline).20 a minute.
GET /v4/abiThe full ABI of every v4 EVM contract and the IDL of every v4 Solana program, as JSON: all of them, or one with ?id= (and &kind=idl for a program), or the list with each file’s sha256 with ?manifest=1; add &download=1 to save it as a file.None.
GET /v4/contractsEvery v4 address the deploy recorded, per chain: its chain id, pcid and contracts by name (EVM addresses checksummed, Solana programs and accounts in base58). Before the first deploy it answers published: false. The top level is the first v4 set (v4a); sets.v4b is the second set (the redeploy new coins launch on), with its own addresses, once it is deployed.None.
POST /tokenomics/v4/accountWhere v4 full scope is switched on (NEXT_PUBLIC_GAP_V4 and NEXT_PUBLIC_GAP_V4_FULL both on; 404 otherwise): what is at one Solana tokenomics account (address): whether it exists, its owner program and its data, read before a creator’s tokenomics setup is built, so a retry reuses the account. Reads only.60 in ten minutes.
GET /coin/[id]/fee-receivers/pendingA fee receiver change waiting to be accepted, per chain.60 a minute.
GET /bridge/healthWhether bridging is working: ok, or the problems.None.
POST /cosignA v3 transfer too large for one part, co-signed by Pangea’s delivery service, with its minimum credited.6 a minute, 30 in ten minutes.
GET /trade/tx-statusWhether an EVM transaction succeeded, failed or is pending.90 a minute.
GET /bridge/activityRecent transfers between chains.None.
GET /bridge/balanceOne address’s balance of a token or the native asset.120 in ten minutes
POST /bridge/quoteA transfer priced, both lanes, with the transaction to sign.120 in ten minutes, 15 a minute.
POST /bridge/solana-txThe unsigned transaction for a transfer out of Solana.60 in ten minutes, 10 a minute.
POST /bridge/solana-fast-txThe same, as a fast order.60 in ten minutes, 10 a minute.
POST /bridge/route-openOpens a Solana route once its setup payment has landed.120 in ten minutes
POST /bridge/arrivalA stream that says when a transfer has arrived.60 in ten minutes
POST /bridge/v4/arrivalVersion-4 coins, where this site has them (404 otherwise): POST { slug, from, to, tx, guid? }, and each transfer of that source transaction toward to comes back landed, held below its minimum (the recipient can accept it at the credit it would get now), accepted, waiting for a Solana price record, or on its way, with the amounts in the token’s own decimals. Reads only; nothing is signed.120 in ten minutes
GET /bridge/v4/historyVersion-4 coins, where this site has them (404 otherwise): ?slug= (and &wallet= for one wallet’s) the coin’s recent v4 transfers, each with what was sent, what was credited and what was burned on arrival, and the accepts, from the tokens’ own events; a chain that did not answer in time is named in unread, never left out silently.120 in ten minutes
GET /bridge/v4/pathA version-4 coin’s stored dollar price path on one chain, where this site has version 4 (404 otherwise): ?slug=&chain=, read from the chain now: none (its pool pairs with the native coin or a dollar token), ok, weak (thin: the dollars a 2% move takes on its thinnest pool, unmeasured, or a pool keeping a single price observation), depth not measured (Solana: the route reads, its depth is not measured yet) or does not read (arrivals priced by it wait or burn until it reads again or someone replaces it), with the quote’s decimals, and whether the path was replaced since launch (anyone can put the launch path back whenever it reads). The path judged is the one the coin prices by, as stored at launch or as replaced since.120 in ten minutes
POST /launch/v4/buy-listA version-4 launch’s Solana list (launch buys on Solana), from this site’s launch page only: op read (the list and its money account as on chain), op blockhash (for the list’s own creator or payer), op band (whether SOL is still within the band the list was quoted at, and whether the launcher’s pool fee and OFT version are still the ones the list sealed). Nothing is built or signed here: the creator’s wallet signs the list.600 in ten minutes
GET /launch/v4/pair-checkA version-4 launch’s pair token, where this site has version 4 (404 otherwise): ?chain=&address=, and what it is before payment: refused only when it truly cannot work (and why: no token code, unreadable decimals or more than the pair maximum, a token that cannot sit in a pool such as NonTransferable or an active transfer hook), not open yet on that side, or ready. Any other token is allowed, at the creator’s risk: a missing or thin pool path to a dollar price is the “weak price path” mark, never a refusal; its own decimals, its transfer tax, and that mark when the price path its launch would use is missing, thin or unmeasured (on EVM: the dollars a 2% move takes on that path’s thinnest pool). On EVM the quote itself measures the pools again and launches with the deepest path; when a pool that could be deeper cannot be measured just now it refuses (“the pools could not be measured just now, try again”) rather than pick around it, and the quote names the path it chose. A path with a pool whose price read comes within 10,000 gas of its venue’s gas cap is not used (a touch could make it stop reading). No pair list exists: nothing here allows or refuses a token by name.60 in ten minutes
POST /launch/v4/self-openA version-4 launch whose pay chain you launch from your own wallet (no Solana leg), where this site has version 4 (404 otherwise): { launchId, chain }, once your turn has come. It builds, and checks, the transactions your fee wallet sends, in order: any “keep two price observations” step a pool the coin’s price rests on still needs (anyone may send it), then the launch itself through that chain’s self-launch opening router, with what the opening owes priced now (a few base units of the pair token, bought with native and paid to the router). Nothing is sent from here, and the router takes the launch only from your fee wallet, with the one signature per chain you gave before paying (it names that chain’s router). If your time runs out, our launcher sends it: same price, same fee.30 in ten minutes
POST /launch/v4/registeredA shared-bridge coin you opened yourself, where this site has the shared-bridge self launch (404 otherwise): { chain, txHash } of the open, once it is mined. This site reads that transaction from the chain itself and lists the coin from what the chain says (its id, home, chain list and transports, its token); nothing you send is stored. The first listing of a coin is kept.20 in ten minutes
POST /launch/v4/sol-openThe Solana side of a shared-bridge coin you open yourself, where this site has the shared-bridge self launch (404 otherwise): { payer }, and the answer is a recent blockhash and the shared lookup table’s addresses, so your wallet can build and sign the open. Nothing is built, signed or sent here.60 in ten minutes
POST /launch/v4/sol-heldOpening Solana later with a wallet the coin’s home chain authorised, where this site has the shared-bridge self launch (404 otherwise): { coinId, homePcid }, and the answer is the bridge’s authorisation slot for the coin on Solana (its address, and its data or null when no authorisation has arrived yet). Your page checks it (the opener must be your wallet, before its deadline) and builds the open itself. Nothing is built, signed or sent here.30 in ten minutes
POST /bridge/refundWhether a fast order is held, and the transaction that sends it back.60 in ten minutes
POST /bridge/ordersTells the board a fast order was posted at a relayer’s price.60 in ten minutes
GET, POST /bridge/relayer-quotesThe relayers’ price board.300 in ten minutes reads, 120 publishes.
POST /chain/txA stream that says when a transaction is in a block.60 in ten minutes
POST /trade/quoteA buy or sell priced through Relay, or Jupiter on Solana.60 a minute.
POST /trade/solana-txCompiles a swap’s Solana instructions into an unsigned transaction.60 in ten minutes
GET /token/launch-buysWho bought in a launch’s own block, per chain, and whether that block has been read.60 in ten minutes, uncached reads only.
GET /coin/[id]One coin: its addresses, degen mode, its supply, and its tokenomics config as its launch signed it. No chain is read.120 a minute.
GET /supply/[token]A coin’s total, circulating and locked supply, per chain and in all, in whole tokens (locked is always 0: every launch pools all of each chain’s share). perChain.supply is the slice the launch minted on that chain, not what is on it after bridging. For a v3 coin, what its bridges burned (the gap fee: a cheap-to-dear bridge withholds part and never mints it) comes off circulating, and off the total too unless the coin's total is fixed (degen); gapBurned says how much. For CoinGecko and CoinMarketCap. A chain that cannot be read is a 503, never a smaller figure.120 a minute.
GET /supply/[token]/totalThe total supply alone, as a bare number (text/plain).120 a minute, shared.
GET /supply/[token]/circulatingThe circulating supply alone, as a bare number (text/plain).120 a minute, shared.
GET /coin/[id]/supplyA coin’s supply: what its launch made, and for a degen mode coin the same read from the chains.60 a minute.
GET /coin/[id]/supply/totalOnly the coin’s total supply, as plain text in whole tokens (the form CoinMarketCap and CoinGecko ask for): every chain’s supply counted once, less what was burned. Held a minute; 503 when the chains cannot be read and no recent figure is held.60 a minute.
GET /coin/[id]/supply/circulatingOnly the coin’s circulating supply, the same way: the total less anything locked or out of public hands (the Solana bridge reserve is never counted, as it backs the other chains).60 a minute.
GET /coin/[id]/tokenomicsA coin’s tokenomics config, and each chain’s vault read from the chain: what waits in each bucket, and what was burned, bought and added to liquidity.20 a minute.
GET /coin/[id]/vault-summaryWhat a coin’s tokenomics vault has done, all time per coin: every vault the coin’s fees have gone to on each chain, earlier ones included (each listed in previousVaults), summed per option in US dollars at today’s prices; an earlier vault not read makes the answer partial and names its chain. 404 for a coin whose fees go to no vault.30 a minute.
GET /coin/[id]/fee-receiversWho a coin’s creator fees are paid to now, read from the chains: each EVM chain’s fee splitter and the Solana launch record, with what each receiver is (a wallet, a tokenomics vault, the burn address or another contract; for a community takeover whose fees are bought back and burned, “buyback”, with no address). Cached for an hour.60 a minute.
POST /v1/launch/prepareA launch quoted, with the ticket every later step takes.120 in ten minutes per address; none with an API key.
POST /v1/launch/authorizationThe permission a creator wallet signs for another launching wallet.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/payThe transaction that pays the launch fee.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/submitHands a paid launch to Pangea’s launcher.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
GET /v1/launch/statusWhere a launch stands, chain by chain.No rate limit; the flood ceiling.
GET /v1/launch/eventsThe same, as a stream of events.3 open at once per address.
GET /v1/launchesEvery coin launched here, newest first, a page at a time.No rate limit; the flood ceiling.
GET /v1/launches/streamEvery new launch the moment it is live, as server-sent events.3 open at once per address.
POST /v1/launch/kitWhat you need to make your own launch-time buys.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
POST /v1/launch/bundle-kitWhere offered: your own launch call on the chain you paid on (Ethereum or BSC), to send first in your own bundle within the window. A v4 launch asks for its kit in the sponsored prepare instead (bundleKitHeld): each Ethereum or BSC chain it opens other than the one you pay on is held for your bundle.120 in ten minutes per address.
POST /v1/launch/solana-self-callYour Solana launch call, when you send Solana’s launch from your own wallet (selfSolana).40 in ten minutes per address (90 for the landed-signature hint).
POST /v1/launch/solana-cosignedNot available yet: answers 409.None.
POST /v1/launch/v4/sponsored/prepareWhere v4 is switched on: a platform-paid v4 launch quoted, on the current v4 contract set, v4b (its Solana leg on the v4b bridge, whose open authorisation the creator's wallet signs as its hex text), with its v4 ticket; its launch buys are asked for here (the v4 launch kit). Payout wallets, one per chain type opened now: creatorEvm (always: the pay chain is an EVM chain) and creatorSvm when Solana opens now (required; a Solana chain only listed is asked when it is opened later). A v4 quote at /v1/launch/prepare answers 409 and names this route.120 in ten minutes per address, with prepare; none with an API key.
POST /v1/launch/v4/sponsored/submitWhere v4 is switched on: hands a paid v4 launch over, with every launch-buy and open signature, each checked against its ticket.300 in ten minutes per address with authorization, pay, submit and kit together; none with an API key.
GET /v1/launch/v4/sponsored/readinessWhere v4 is switched on: which chains a platform-paid v4 launch can open now.No rate limit; the flood ceiling.
POST /v1/launch/v4/openWhere v4 is switched on: the creator's own v4 leg of a queued launch, on its pay chain, priced now: { launchId, chain } answers { pre, tx, owed, quote, via, takeoverAt } for the creator's wallet to send before takeoverAt. Not an open-later: that is POST /v1/launch/v4/open-later.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/open-laterWhere the full-scope switch is on: opens another listed chain of a coin already launched, at any time, on the coin's own contract set (a v4a coin stays on v4a): { id, chain, step: "authorize" } by the current creator on the home chain (with the payout wallet first when that chain type has none), then { id, chain, step: "open" } by the authorised wallet; each answers the transactions for your own wallet.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/registeredWhere the full-scope switch is on: lists a mined v4 open from the chain itself: { chain, txHash }, or { chain: "solana", coinId }; answers { slug, coinId }, 422 NOT_LISTABLE when the site will not list its name or symbol.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/tokenomicsWhere the full-scope switch is on: the same as POST /tokenomics/v4/switch (on, off, clear; the role push from the coin's home chain, EVM or Solana, and its read-back; for a Solana-home coin the open authorisation), built for your own wallet.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/handoverWhere the full-scope switch is on: the creator handover (op: "transfer" with newEvm and optional newSvm, or "renounce") on an EVM-home coin's home chain, for the current creator's wallet. A Solana-home coin answers 409: its handover is signed on the coin page.300 in ten minutes per address with the chain-read steps together; none with an API key.
POST /v1/launch/v4/sol-kitWhere v4 is switched on: the Solana bundle kit of a platform-paid v4 launch. With your v4 ticket: the signed Solana launch while it is held for you; with your bundle (that launch first, then 1 to 4 signed buyer transactions, the last paying the Jito tip): checked and sent to Jito as one bundle. Refused by the relay: the launch goes alone, with its launch buys.300 in ten minutes per address with authorization, pay, submit and kit together, and 120 in ten minutes per IP; none with an API key.
The launch endpoints under /v1/launch are described in Launch API →

GET /coin/{id}

Coin, GEThttps://www.pangea.trade/api/coin/{slug, launch id or token address}

One coin as the site knows it, with no chain read: { slug, name, symbol, launchId, listedAt, launchChains, chains, degen, factoryGen, supplyMode, factoryV3, supply, tokenomics, tokenomicsKnown }. chains is each chain’s { token, pool, quote }; quote is the asset its pool pairs with: "native", or { address, ticker, symbol, issuer, known } for a coin paired with a tokenized stock on that chain, found by the stock token's address (known: false for an asset the stocks table does not list). A stock-paired EVM chain can show pool: null until its pool is listed. factoryGen is 1 or 2; factoryV3: true marks a factory v3 coin (absent before v3); supplyMode is factory v2’s, 0 (1B on each chain) or 1 (degen mode), and null on a factory v1 coin. tokenomics is the config the launch’s quote signed, or null for none; tokenomicsKnown: false means it could not be read, so null is then not “none”. Errors: 400 for an id that cannot be a coin’s, 404 for one not launched here.

FieldWhat it is
degentrue for a coin launched in degen mode: 1B in all, an equal share minted and paired on each chain, every chain’s token reporting the 1B. Degen mode →
supply.totalThe launch’s whole supply, in whole tokens, as a string: 1B for degen mode (999,999,999 on three chains, whose equal shares are whole tokens), 1B a chain otherwise.
supply.circulatingWhat the chains minted and paired, in all: 1B in degen mode. The bridge reserve backs the other chains and is not extra supply, so it is never taken off circulating; /coin/{id}/supply’s live takes off what was burned and reads the bridge reserve as it is now.
supply.reserve, supply.reserveChainThe rest of a degen mode coin’s supply, held in Solana’s bridge reserve, and solana. 0 and null without one.
supply.perChainWhat each chain minted and paired, by chain.
supply.reportedPerChainWhat every chain’s totalSupply() reports in degen mode, the 1B; null otherwise, where each chain reports its own.
supply.circulatingShareBpsIn degen mode, what one chain mints as a share of the total, in basis points: 2,000 on five chains. 10,000 where each chain holds a whole supply of its own; null where the supply figures are.
supply.supplyModelperChain for 1B on each chain, per-chain for a v4 coin (1B per opened chain), global for one supply (degen mode, and launches before 2026-10-15, whose figures are null here: /pools reads them off the token).
tokenomics.perChainEach chain’s split by bucket, in basis points: { burn, buyback, liquidity, creator, community }, adding up to 10,000.
tokenomics.vault, tokenomics.ownerThe coin’s vault on the EVM chains, the fee receiver its trading fees are paid to, and its owner, the creator, who receives the creator share.
tokenomics.treasury, tokenomics.communityThe wallets the bought-and-held coins and the community share go to on the EVM chains; null when no chain uses that bucket.
tokenomics.solana{ account, owner, treasury, community }: the coin’s tokenomics account on Solana and its wallets there; null without Solana.
tokenomics.version1 for the config above. 2 for tokenomics v2: perChain is then each chain’s { burn, buyHold, liquidity, holders, treasury, wallets, rewards } (shares in basis points; wallets the payout wallets with theirs, rewards the holder rewards’ tokens, native for the chain’s own coin), with vault, account (Solana), owner, ownerSol and holders (who qualifies). Another value is a kind this API does not describe yet, and carries only version and vault.

GET /coin/{id}/supply

Supply, GEThttps://www.pangea.trade/api/coin/{id}/supply

The answer is { slug, name, symbol, launchId, launchChains, degen, supply, live, liveError? }. supply is the same as on the coin. For a degen mode coin, live is read from the chains and kept a minute: { mode, total, reserve, circulating, burned, burnedUnread, perChain, burnedPerChain, reserveAddress, decimals, onSolana, chains }, in base units as strings, where total is what the tokens report, reserve what Solana’s bridge reserve at reserveAddress holds now, and circulating the total less what was burned (never less the bridge reserve, which backs the other chains and is not extra supply); perChain is what is on each chain, Solana’s its own float with the bridge reserve apart. A figure the chains did not give is null. live is null for any other coin and with ?live=0; liveError: "busy" means the shared budget of chain reads is spent for the minute.

GET /coin/{id}/tokenomics

Tokenomics, GEThttps://www.pangea.trade/api/coin/{id}/tokenomics

The answer is { slug, name, symbol, launchId, launchChains, tokenomics, tokenomicsKnown, chains }. tokenomics is the coin’s config, as on the coin. chains is each chain’s vault read from the chain, { tokenomics }, or null with an error when that chain could not be read. A vault has its split, buckets (what waits in each, in the chain’s native unit), spent, and totals: receivedWei, coinBurned, coinToTreasury (bought and held), liquidityAdded, gasRepaidWei. A coin its launch’s config says has no tokenomics is read on no chain. ?live=0 answers the config alone. Each chain’s reading is shared with the coin page and kept a while: a vault five minutes, no vault 24 hours, a chain that could not be read half a minute. 404 while tokenomics is switched off on this deployment.

GET /v1/launches

Launches, GEThttps://www.pangea.trade/api/v1/launches?limit=25

Every coin launched here, newest first: { launches, next, latest, readAt }. Each launch is { id, slug, name, symbol, launchId, listedAt, launchedAt, launchChains, chains, coinId, v4, degen, factoryGen, supplyMode, factoryV3, supply, tokenomics, image, platform }: chains each chain’s { token, pool, quote } (quote as in GET /coin/{id}), factoryV3 true for a factory v3 coin, supply is { total, circulating, reserve, reserveChain }, tokenomics is { on: false }, or { on: true, version, vault, buckets, chains } naming the buckets any chain uses, or null while it is not known; image the coin’s logo on this site, or null until it is stored. For a v4 coin, coinId is its coin id and v4 is { coinId, home, listed, open, notOpen, transports, role: { creatorEvm, creatorSvm, renounced } }: its home chain, the chains it lists, which of them are open now, its transports and its creator role; each open chain carries openedAt, unix seconds. Both are null for any other coin. A coin is listed once it has landed and its pools are open. id is its cursor, <listedAt>:<slug>.

  • limit: 1 to 100, 25 by default.
  • before: the previous page’s next, for the page after it. next is null on the last page. To catch up, send the same since with each page.
  • since: a cursor, for the coins listed after it, or unix seconds, for the coins listed in that second or later. A coin can be listed a little after one with a later time, so to catch up, ask from a minute or two before the newest you have and drop the ids you already hold.
  • latest: the newest coin’s cursor. Open the stream with it as since and nothing is missed between the two.
The newest launches, then the page after
curl -s "https://www.pangea.trade/api/v1/launches?limit=10"
curl -s "https://www.pangea.trade/api/v1/launches?limit=10&before=<next>"

GET /v1/launches/stream

Launches stream, GEThttps://www.pangea.trade/api/v1/launches/stream

Every new launch as server-sent events, the moment it is live. Each is an event: launch with the launch, as /v1/launches lists it, as its data, and, for a launch newer than any sent before, its cursor as the event id. After the first catch-up comes one event: ready with { latest }. To miss nothing across a reconnect, send the last id you saw as Last-Event-ID (an EventSource does this by itself) or as ?since=: the launches listed after it come first, oldest first, up to 100 at a time. Without either, only launches from now on. The ready event carries the stream’s place as its id too, so a reconnect before any launch still resumes. A coin can become visible a little after one listed later, so the stream also looks two minutes back and sends such a coin with no event id, keeping your place. A resume sends those two minutes again, so a launch can come twice: drop the ones whose data’s id you have. When a v4 coin already sent opens another of its listed chains later, the stream sends an event: chain_opened for that same launch, never a new launch: its data is { key, coinId, chain, openTime, slug, id, launch }, keyed by coin id, chain and open time, with no event id. Clients dedupe by coinId: a key you already hold is a repeat. A resume sends again each chain opened after your cursor’s time. A comment line comes every 15 seconds; the stream closes after 30 minutes, so reconnect with the last id. Send Accept: text/event-stream. At most 3 open per caller; it takes requests from programs and from the Pangea site, like the Launch API.

Server-sent events are the simplest realtime API: one long GET that any HTTP client can read, that resumes by itself, and that passes every proxy. For pools, trades and bridges as they happen, open the live WebSocket below.
Follow new launches with curl
curl -N -H "Accept: text/event-stream" "https://www.pangea.trade/api/v1/launches/stream"
Follow new launches with EventSource (Node: npm install eventsource)
import { EventSource } from "eventsource";

const stream = new EventSource("https://www.pangea.trade/api/v1/launches/stream");
stream.addEventListener("launch", (e) => {
  const coin = JSON.parse(e.data);
  console.log(coin.symbol, coin.degen ? "degen" : "", coin.chains);
});
// It reconnects by itself and sends the last id as Last-Event-ID, so nothing is missed.

WebSocket /live

The socket the site’s own pages use, open to everyone and read-only: wss://www.pangea.trade/live. No key, any origin or none. You subscribe to topics and are pushed what changes: pool readings, trades, bridges and new coins. Everyone subscribed to a topic is sent the very same message, made once, so hold as many sockets as you need, up to 50 from one IP address.

It is a wake-up channel, not a second source of truth: every value on it is also the answer of a REST endpoint here. A client that misses something on the socket is right again after its next REST read, and the rules below say when one is needed.

TopicWhat it carries
poolsdiffLog: the pool reading as small patches, about once a second while anything trades. See the pool reading, as patches, below.
poolsState: the pool reading of GET /pools. It is larger than one message may be, so it arrives as reset with size: follow poolsdiff instead.
pools:<slug>State: one coin’s pools, the same shape with one launch in it, replaced each time.
launchesState: the coin list, { readAt, launches: [{ slug, name, symbol }] }. A new coin appears here.
trades, trades:<slug>Log: one event per trade, of every coin or of one. Cursor letter s.
bridges, bridges:<slug>Log: one event per bridge transfer. Cursor letter b.
healthState: whether the feeds behind the socket are up. While delayed is true, poll REST.
job:<launchId>State: a launch job’s status, as the launch status route answers it. It is pushed again on every step of every chain, as it happens: see A launch, step by step, below.
relay:<requestId>The site’s own pages only: asked for from anywhere else, the socket is closed with 1008.

A launch, step by step

  • Subscribe to job:<launchId> (the launch id in lowercase) from the moment the fee is paid. Each message is the whole status, so replace what you hold.
  • Per chain, d.result.sponsoredLegs while the launch runs (d.result.legs once it is finished) holds { state, step, at, ms?, tx?, why?, listed? }.
  • step is one of paid (the fee is confirmed and the chain is queued), sending, sent (tx is the open’s hash, or its signature on Solana), confirmed, open (the pool is live), failed (why says why) or handover (the creator opens that chain from their own wallet). Steps only move forward.
  • at is when the step happened, in milliseconds since the epoch. ms is how long the chain took. listed: true means that chain is in the coin list.
  • d.result.coinId and d.result.coinListedAt appear the moment the coin is listed, before the job is done. From then on GET /coin/[id] answers.
  • The chain the fee was paid on opens first. Every other chain then opens at the same time.

Messages

  • You send JSON text of at most 1024 bytes: {"op":"sub","topics":["trades","poolsdiff"],"cursor":"s:1200,p:1762150000000123"}, {"op":"unsub","topics":["trades"]} or {"op":"ping"}. At most 12 topics on one socket. Anything else closes the socket with 1008.
  • The server first sends {"t":"hello","v":1,"now":ms,"hbMs":15000,"maxTopics":12,"sources":{...}}.
  • A state topic sends {"t":"pools","topic":"pools:abc","d":{...}} (and likewise launches, health, job): replace what you hold. On subscribing you are sent the current value at once.
  • A log topic sends {"t":"trade","topic":"trades","seq":1201,"slug":"abc","d":{...}} (and likewise bridge, poolsdiff): apply it only if its seq is above your cursor for that stream, then move the cursor to it.
  • {"t":"ready","topics":[...],"cursor":"s:1250"}: the replay of those log topics is done and they are live; the cursor is where each stream now stands.
  • {"t":"reset","topics":[...],"why":"replay"}: you are not subscribed to those topics. Read them over REST, then subscribe again with the cursor that read gives. why is replay (more than 1000 rows behind), size, ahead, unavailable or busy (wait a few seconds first).
  • {"t":"hb","now":ms} comes every 15 seconds. If nothing at all arrives for 40 seconds, reconnect.
  • {"t":"bye","reason":"rotate","retryMs":2100} comes before the server closes the socket: rotate (every socket is closed after about 30 minutes), slow, shutdown or rate. Reconnect after retryMs.

Reconnecting without missing anything

  • Keep one cursor per stream: the seq of the last trade (s), bridge (b) and pool patch (p) you applied, and subscribe with them, as "cursor":"s:1200,b:77". You are sent every row after them, then ready, then live: no gap and no repeat. Without a cursor a log topic starts from now, and ready gives you the cursor to keep.
  • When the socket closes without a bye, wait a random time between zero and a ceiling that starts at half a second and doubles up to 30 seconds, reconnect, and subscribe again with your cursors.
  • Slow readers are cut off, not waited for. A socket holding 256 KiB unread is not written to; meanwhile states keep only their newest value, and past 500 queued events the socket is told bye with slow. Reconnect with your cursors and nothing is lost.
  • Messages you send are counted per socket, a burst of 60 and then 10 a second, pings included. Past that the socket is told bye with rate.
  • Compression (permessage-deflate) is offered, and most clients take it on their own. The messages are the same either way.

The pool reading, as patches

  • Read https://www.pangea.trade/live/pools: { gen, body }, where body is a pool reading in the shape of GET /pools. Then subscribe to poolsdiff with "cursor":"p:<gen>". It answers 60 reads a minute from one address: read it at the start and after a reset, not on a timer.
  • Each poolsdiff event’s d.base is the gen it applies to, and its seq is the gen it makes. Apply it only on top of exactly the gen you hold. When d.body is present it is a whole new reading.
  • Otherwise d is { base, top, set, order }. top holds the reading’s top-level fields. Each entry of set is a launch by slug: whole as v, or its changed fields as f and its changed pools as pools, matched by chain (each whole as v or as changed fields f). order, when present, is the new order of the slugs.
  • A patch on a gen you do not hold, a reset of poolsdiff, or a patch naming a launch or pool you do not have: read https://www.pangea.trade/live/pools again and subscribe with its gen.
Follow trades and pools over the live socket (Node 22 or later, or a browser)
const LIVE = "wss://www.pangea.trade/live";
const BASE = "https://www.pangea.trade/live/pools";

let pools = null; // { gen, body }
let tradeCursor = null;
let attempt = 0; // reconnects since the last ready
const waits = {}; // topic -> how long to wait before asking for it again

function applyPatch(body, d) {
  const bySlug = new Map(body.launches.map((l) => [l.slug, l]));
  for (const e of d.set) {
    if (e.v) {
      bySlug.set(e.slug, e.v);
      continue;
    }
    const old = bySlug.get(e.slug);
    if (!old) return null;
    const l = { ...old, ...(e.f ?? {}) };
    if (e.pools) {
      l.pools = old.pools.slice();
      for (const pe of e.pools) {
        const i = l.pools.findIndex((p) => p.chain === pe.chain);
        if (i === -1) return null;
        l.pools[i] = pe.v ?? { ...l.pools[i], ...(pe.f ?? {}) };
      }
    }
    bySlug.set(e.slug, l);
  }
  const order = d.order ?? body.launches.map((l) => l.slug);
  return { ...d.top, launches: order.map((slug) => bySlug.get(slug)) };
}

async function readBase() {
  const res = await fetch(BASE);
  if (!res.ok) throw new Error(`GET /live/pools answered ${res.status}`); // 429 or 503: try again later
  pools = await res.json();
}

// Ask for one topic again later, waiting longer each time (2 s, doubling to 60 s).
function later(ws, topic) {
  waits[topic] = Math.min(60_000, (waits[topic] ?? 1_000) * 2);
  setTimeout(async () => {
    if (ws.readyState !== WebSocket.OPEN) return;
    try {
      if (topic === "poolsdiff" && !pools) await readBase();
      subscribe(ws, [topic]);
    } catch {
      later(ws, topic);
    }
  }, waits[topic]);
}

function subscribe(ws, topics) {
  const cursor = [];
  if (topics.includes("trades") && tradeCursor !== null) cursor.push(`s:${tradeCursor}`);
  if (topics.includes("poolsdiff")) cursor.push(`p:${pools.gen}`);
  ws.send(JSON.stringify({ op: "sub", topics, cursor: cursor.join(",") }));
}

async function connect() {
  try {
    if (!pools) await readBase();
  } catch {
    setTimeout(connect, Math.random() * Math.min(30_000, 500 * 2 ** attempt++));
    return;
  }
  const ws = new WebSocket(LIVE);
  let heard = Date.now();
  let retryMs = null; // from bye
  const watch = setInterval(() => Date.now() - heard > 40_000 && ws.close(), 5_000);
  ws.onopen = () => subscribe(ws, ["trades", "poolsdiff", "launches"]);
  ws.onmessage = (e) => {
    heard = Date.now();
    const m = JSON.parse(e.data);
    if (m.t === "trade" && (tradeCursor === null || m.seq > tradeCursor)) {
      tradeCursor = m.seq;
      console.log("trade", m.slug, m.d.side, m.d.valueUsd);
    } else if (m.t === "poolsdiff" && pools && m.seq > pools.gen) {
      const body = m.d.body ?? (m.d.base === pools.gen ? applyPatch(pools.body, m.d) : null);
      if (body) pools = { gen: m.seq, body };
      else {
        pools = null; // behind: start again from the base, on the same socket
        later(ws, "poolsdiff");
      }
    } else if (m.t === "ready") {
      attempt = 0;
      for (const t of m.topics) delete waits[t];
      const s = /s:([0-9]+)/.exec(m.cursor ?? "");
      if (s && tradeCursor === null) tradeCursor = Number(s[1]);
    } else if (m.t === "reset") {
      // Not subscribed to these any more. Keep the socket; ask again later, waiting longer each time.
      for (const topic of m.topics) {
        if (m.why === "replay" || m.why === "ahead") {
          if (topic === "trades") tradeCursor = null; // too far behind: fill the gap from GET /history
          if (topic === "poolsdiff") pools = null;
        }
        later(ws, topic);
      }
    } else if (m.t === "bye") {
      retryMs = m.retryMs;
    }
  };
  ws.onclose = () => {
    clearInterval(watch);
    setTimeout(connect, retryMs ?? Math.random() * Math.min(30_000, 500 * 2 ** attempt++));
  };
}

connect();

GET /holders

Holders, GEThttps://www.pangea.trade/api/holders

Query: token, a slug, required; chain, optional, to answer for one chain. The answer is { slug, readAt, launchTotal, degen, supply, chains, all }, where degen and supply are as on /coin/[id], and each of chains is { chain, count, top10Pct, rows, complete, note?, error? } and each row { chain, address, balance, pct, label }, balances in whole tokens, at most 100 rows a chain. all combines the chains, { rows, count, partial, top10Pct }, and is left out when chain is given. label names a known holder such as the pool or the bridge escrow. An answer is reused for a minute. Errors: 400 without a token, 404 for an unknown launch or a chain it is not on.

GET /bridge/balance

Balance, GEThttps://www.pangea.trade/api/bridge/balance

Query: token (slug), chain and address, all required, and asset=native to read the chain’s native asset instead of the token. The answer is { slug, chain, address, balance, decimals, reason?, reserve? }: balance in base units as a string, or null with a reason when the chain could not be read, never a false zero. On Solana the token balance is the sum of the owner’s token accounts for the mint. For the native asset, reserve is the gas the app keeps back for a trade. Errors: 400 for a missing field or an address of the wrong kind, 404 for an unknown launch or chain.

POST /bridge/quote, in full

Beyond the fields in For integrators, the body may carry slippageBps for the fast lane, 50 unless given and 1000 at most, and smartWallet, true when the sender is a contract wallet, and gapToleranceBps, a factory v3 token's tolerance under the gap estimate for the minimum credited (100 unless given, 2000 at most). The answer’s routes holds two routes, canonical and fast, and a route that cannot be used says why in unavailable. Besides the fields listed there, a route may carry:

FieldWhat it is
labelStandard or Fast.
fastFee, relayerFee, relayerOn the fast route: the fast lane’s fee, the relayer’s price and who it is, in base units.
minReceivedOn the fast route: the least the order lets the recipient be paid.
approvalOn the fast route from an EVM chain: the approval to send first, { to, data, value, chainId }, or null when none is needed.
solanaFastOn the fast route out of Solana: { relayerFee, minReceived, nativeFeeCap }, for /bridge/solana-fast-tx (nativeFeeCap: the most the order signs for LayerZero, also the route's nativeFee).
fillDropFee, layerZeroFee, fillDropNoteA fast order into Solana: the Fill drop (the instant fill's account rent, paid once to Pangea's delivery) and LayerZero's part of nativeFee.
routePaymentFrom an EVM chain into a Solana route that is not open: the one-time setup payment, { to, data, value, chainId }, to send before the transfer.
setupNote, messageFeeLaterOut of Solana with a route to open: the setup is paid first and LayerZero’s fee is priced after it.
feeIsMaximumnativeFee is the most the Solana transaction will sign; LayerZero charges what its workers quote.
noteA sentence about the route, for showing to a person.
gapA factory v3 token's standard route: the price-gap estimate. state (estimate, free, held or unavailable), burned (what the destination withholds, in decimals), minCredited and toleranceBps (the least it may credit, and the tolerance it was set with), minCreditedSource (out of Solana, in Solana's decimals), cover (the gas cover, when it may apply), source (a Solana source's price record), delivery (into Solana: { amount, note }, our delivery key's cost withheld from the arrival, in decimals; its ata, when present, is the part that is a new wallet's token-account rent on its first arrival) and poke (an EVM source's price record to send first), and warning (when set: a sentence to show before the send -- e.g. an arrival too small to pay a new wallet's token-account rent, which waits on Solana until that account exists; web-r4-b I-5). What arrives is amountOut less burned and, into Solana, less delivery.amount.
keysOnlyDeliveryFee, keysOnlyDeliveryNoteA factory v3 send into Ethereum: the part of nativeFee that pays Pangea's delivery there (in the source's wei; LayerZero's executor does not deliver there). Paid once, inside the same transaction.
dstDeliveryFeeOut of Solana toward Ethereum (a factory v3 token): the part of nativeFee that pays Pangea's delivery there, in lamports.
deliveryFee, deliveryPayeeThe site's instant-delivery fee inside nativeFee (when on), and the payee the send's refund address names for it.
needsRecordA factory v3 send out of an EVM chain whose price log needs records (GapNeedRecords): { pokes, poke }. call is null and gap.poke carries the same { to, data, value, gas }: send it from the sender's wallet about pokes times, one mined before the next, then ask the quote again.
splitA factory v3 send above what one transfer carries now: { text, count, minutes, parts, maxSendLD, cosign, cosignTx?, cosigner?, perPartFee, totalFeeEstimate, perPartLines, partGap? }. call is null. Every part is its own transfer and pays the full per-send fees: perPartFee (the first part's quote, itemised in perPartLines) and totalFeeEstimate (perPartFee x count). From an EVM chain, cosignTx is the whole send, unsigned, for POST /api/cosign ({ chain: "evm", chainId, tx, gapToleranceBps? }): its answer is the co-signed send with its fee (nativeFee, tx.value) and the minimum it guarantees at the co-signed price (minCreditedLD); a 503 means send in parts. From Solana, cosign is true only when the co-sign service is set up and cosigner names the GapGlobal co-signer: POST /api/bridge/solana-tx with cosign: true (and minCredited) builds the unsigned co-signed send (stage "cosign"), POST /api/cosign ({ chain: "solana", tx, gapToleranceBps? }) co-signs it and answers minCreditedLD and expectedMinCreditedLD; the wallet signs last. Before it signs, compare the answer's message bytes with the ones you built: they must be identical, with your wallet's signature slot still empty and only the co-signer's filled (the proxy never sees what you built, so a client of POST /api/cosign makes this check itself, as the site's own panel does). Any 503, ABOVE_COSIGN_MAX or mismatch means send in parts.

Further errors: 409 when the token has not launched on the destination, 424 when a Solana recipient could not be checked, and 424 with retry: true when pricing took longer than 45 seconds.

POST /bridge/solana-tx

Out of Solana, POSThttps://www.pangea.trade/api/bridge/solana-tx
The body you POST
{
  "slug": "<launch slug>",
  "to": "base",
  "amount": "1000000000000",
  "sender": "<Solana wallet>",
  "recipient": "0x<EVM address>"
}

Builds the unsigned transaction that sends the token from Solana to an EVM chain, for the sender to sign. For a token with the price-gap fee, minCredited is required: the least the destination may credit after it, in Solana base units (the bridge quote’s gap.minCreditedSource); without it the request is refused (400). after and afterValidUntil are given only on a second call after a setup step. The answer carries the request’s from, to, amount and symbol and one of three stages:

StageWhat to do
sendSign transaction, a base64 versioned transaction, and send it. received is what arrives, nativeFee LayerZero’s fee and nativeFeeCap the most it will sign.
togetherThe same, with the route’s opening in the same transaction. setupFee is the opening’s part of totalFee.
setupThe opening alone. Sign and send it, then call again with after set to its signature for the send.
recordA factory v3 token whose Solana price log holds no recent price: sign transaction (record_price, its signature fee only), send it, then call again with recorded set to its signature. Until it has landed and a slot has passed the answer is 202 with { pending: true, stage: "recording" }; then it is the send.

On Solana a factory v3 arrival is delivered with 650,000 compute units. A charged v3 receive there runs only through LayerZero's executor (its own execute flow) or Pangea's delivery keys (R1′), so nobody can land it at a price of their choosing; an escaped, refunded or unpooled arrival stays open to anyone.

Every stage carries blockhash and lastValidBlockHeight: sign within about a minute. While a setup step is still settling the answer is 202 with { pending: true, stage: "waiting" }; ask again. Errors: 400 for a bad field or a signature that is not this transfer’s setup, 404 for an unknown launch, 409 when the setup ran out or failed, the route is unusable or the wallet is short of SOL, 424 when the build takes longer than usual, with retry: true, and 424 after 50 seconds.

POST /bridge/solana-fast-tx

Fast out of Solana, POSThttps://www.pangea.trade/api/bridge/solana-fast-tx

The fast lane out of Solana: one unsigned transaction holding Pangea’s fast-bridge order and the OFT send that carries it. Body: { slug, to, recipient, amount, relayerFee, minReceived, sender }, with relayerFee and minReceived from the quote’s solanaFast. The answer is { stage: "send", transaction, orderId, amount, arriving, fastFee, relayerFee, nativeFee, nativeFeeCap, blockhash, lastValidBlockHeight }. Errors: 409 when the fast lane is off for this launch or route, 422 when the order could not be built, nothing having been sent.

POST /bridge/route-open

Open a Solana route, POSThttps://www.pangea.trade/api/bridge/route-open

The second step of the first transfer from an EVM chain into a Solana route: once the quote’s routePayment has been sent, this hands its transaction to Pangea’s delivery service, which checks the payment on chain and opens the route, or refunds it. Body: { slug, from, txHash, sender }, from being the EVM chain the payment was made on. With Accept: text/event-stream it streams { state, reason?, refundTx? } as the route is opened; otherwise it answers once. state is one of verifying, paid, opening, open, refunded, refund-owed, review and refused. Send the transfer once it is open. Errors: 404 for a launch not on Solana, 424 when routes cannot be opened from this deployment.

POST /bridge/arrival

Arrival stream, POSThttps://www.pangea.trade/api/bridge/arrival

Body: { txHash }, the send’s transaction hash or Solana signature. A stream of { state, route, guid, dstTx?, by? }, state being in-flight and then delivered, with the delivery’s transaction in dstTx. It is fed by Pangea’s delivery service, which remembers a transfer for 30 minutes. Errors: 400 for a malformed hash, 424 when the deployment does not serve arrivals; LayerZero Scan answers the same question.

POST /bridge/refund

Held fast orders, POSThttps://www.pangea.trade/api/bridge/refund

Body: { chain, orderId }, the destination chain and the fast order’s id. The answer is { held: false } or { held: true, guid, amount, srcEid, call }, where call is the refund(guid) transaction, { to, data, value, chainId }, with LayerZero’s fee for the trip back as its value. It looks back 5,000 blocks. Errors: 400 for a malformed id or a chain without a fast bridge.

POST /bridge/orders

Report a fast order, POSThttps://www.pangea.trade/api/bridge/orders

Body: { from, hash }, the chain a fast order was posted on and its transaction. The board reads the order from the receipt and remembers whose price it was sent at, which is how a relayer that does not fill is benched. The answer is 202 with { recorded: false, pending: true } before the receipt exists, then { recorded, relayer, why?, orderId, to, srcBridge }. Only orders priced from this deployment’s board are recorded; why says why not.

POST /chain/tx

Transaction stream, POSThttps://www.pangea.trade/api/chain/tx

Body: { chain, hash }. One event when the transaction is in a block: { state: "mined", success, block } on an EVM chain, { state: "mined", success } on Solana, or { state: "unseen" } if it never appears, after up to 10 minutes on an EVM chain and 5 on Solana.

Trading: POST /trade/quote and POST /trade/solana-tx

These are the trade card’s own and may change; read a quote for a price, and build trades from the pools directly where you can. /trade/quote takes { slug, side, chain, amount, user?, solanaUser?, slippageBps?, mode?, intent?, quoteAsset? }: side is buy or sell; chain is the chain the wallet trades on, always a chain name (auto is refused, and a fromChain other than chain is refused too); amount is the pay asset’s base units for a buy, the token’s for a sell. slippageBps is the slippage, how far below the quote the trade may fill: a whole number of basis points from 1 to 9900 (0.01% to 99%), 100 unless given. mode is "direct" (the default: the coin’s own pool on that chain) or "cheapest" (also price the other chains’ pools and offer the route when it is better after every fee); intent is "preview" (a price, nothing to sign) or "execute"; quoteAsset: "stock" pays or receives the stock token itself on a stock-paired EVM chain. A direct trade goes through the coin’s own pool (details.router: "pool"), with Jupiter as a fallback on Solana. The answer is { mode, preview?, chain, fromChain, crossChain, cheapestNote, unroutable, details, steps, cheapest? }; with mode: "cheapest", cheapest is { offered, reason, plan, steps, comparison: { directOut, routeOut, gainBps, cost }, tried } and, when it is offered, directDetails is the direct trade beside it. Cheapest mode has its own brakes (20 a minute per wallet, 2 at once per caller); past them it answers as a direct quote with the reason in cheapestNote. Errors: 400, 404, 422 with the reason when no route was found, and 424 when a chain or the router could not be reached or took too long (with retry: true where trying again can help). Never a 5xx: a proxy in front may replace a 5xx body.

/trade/solana-tx takes { instructions, addressLookupTableAddresses?, payer }, a swap step’s Solana instructions as the quote gives them, at most 24 instructions and 8 lookup tables, and answers { transaction, blockhash, lastValidBlockHeight }: an unsigned versioned transaction. It does not inspect what the instructions do, so read them, or let your wallet simulate them, before you sign.