# Ascend integration guide

Ascend's [GitBook](https://ascendlaunch.gitbook.io/docs) contains the protocol
litepaper. This guide covers public contract integration and token metadata for
**Elysium mainnet, chain 99901**, where Ascend runs on Uniswap v4.

The mainnet developer reference (contract addresses, ABIs, events and a launch
and trading walkthrough) is on [GitBook](https://ascendlaunch.gitbook.io/docs/developers).

## Current deployment and interfaces

Start with the
[contract registry](/contracts.json) and the
[trading deployment](/deployments.json). The trading document must name chain
99901 and `generation: "v5"` before its addresses are used: this deployment runs
the current fee plan, where every launch goes through the escrow factory
(`escrowFactory`) and each pool's creator role is its own escrow. It also names
the seat factory (`seatFactory`), the CTO registry (`ctoRegistry`), the RuleSet
new launches copy (`ruleSet`) and the token factory (`tokenFactory`).

Each release publishes its ABIs as one versioned file, named for the
generation and the record: [/abis/ascend-v5-99901-422319.json](/abis/ascend-v5-99901-422319.json).
It holds the full ABI of every contract type in the registry, plus each pool's
`CreatorEscrow`, a seat coin's `CreatorSeatV5` and the coin (`AscendTokenV5`),
with the registry rows of each type. Integrate with these ABIs and the
registry's addresses. Ascend publishes no SDK or client package.

## Network and contracts

- Network: Kinetiq Elysium, chain ID **99901**.
- Gas: HYPE, 18 decimals, bridged from HyperEVM ([bridging guide](https://elysium.kinetiq.xyz/docs/token-bridging-on-elysium)).
- Public RPC: `https://rpc.ascendlaunch.xyz`.
- [Explorer](https://elysium.kinetiq.xyz/explorer).
- Published address registry: `/contracts.json` on the app host.

This deployment's quote assets are the `quotes` rows of the trading deployment.
A HYPE pair quotes in WHYPE (shown as HYPE in the app): wrap HYPE with WHYPE's
`deposit()` before a buy, and the app does this for its users.

The registry lists the contracts the current deployment uses, by type and
address. A release can replace contracts. Match the chain ID and contract bytecode before sending a
transaction. Use your own EVM provider for sustained application traffic.

Contract addresses and token addresses are the identifiers. Names and symbols
can repeat. The launchpad's `Launched` event identifies each new token and pool.
Use the event from the expected launchpad in a successful transaction receipt.

Each pool has its own trade fee. The creator chooses it at launch, from 1% to
10% (10,000 to 100,000 pips), and it cannot change afterwards; a launch through
an approved builder adds the builder's fee, at most 1%, on top. The builder's
fee is an add-on: a 2% coin through a 0.5% builder charges 2.5%, the builder is
paid its part off the top, and the creator's and protocol's shares of the 2% do
not change. Never assume a constant: read `currentFee(poolId)` on the launchpad
(the pool's configured fee from launch for life). The `Launched`
event carries the pool's whole fee and the builder's part.

Ascend's own swap interface charges a 0.25% interface fee, taken from the
output, on swaps that use no Ascend launch pool, where it is enabled; a route
through a launch pool pays that pool's fee instead, never both. The contracts
charge no interface fee: a transaction you build pays only the pools' fees.

Every fee is taken in the pair asset (the quote), by the hook, on buys and
sells alike. The pool's stored LP fee is 0 for life: `StateView.getSlot0(poolId)`
and the PoolManager's `Swap` log both report 0, and `Swap` amounts are the AMM's
share alone. Simulate with V4Quoter, which charges the hook's fee exactly, or
compute it from `f = currentFee(poolId)`, with `q` the quote the pool moves:

- exact-input buy: `ceil(in * f / 1e6)` of the quote paid in; the AMM swaps the rest;
- exact-input sell: `ceil(out * f / 1e6)` of the quote the AMM pays out;
- exact-output buy: `ceil(q * f / (1e6 - f))` on top of the AMM's quote in;
- exact-output sell: `ceil(q * f / (1e6 - f))` on top of the quote asked for.

A swap a price limit or the end of the curve stops short pays the formula on
the quote that traded (for a specified quote: on top of the AMM's quote in, or
`ceil(q * f / 1e6)` of its quote out); donations revert `NoDonations()`. The
launchpad's `HookSwap` event carries each swap's whole hook delta and fee.
Uncollected fees are `accruedFees(poolId)` on the launchpad; `collect(poolId)`
books them to the pool's escrow and `claim(poolId)` pays the escrow in the quote.
The escrow splits each arrival by the rules the pool launched with (`Settled`)
and pays its recipient through `claim(account)`.

## Aggregators and routers

This fee model requires the no-tax hook: `launchFeeWindowSeconds()` returns zero,
and its verified deployment record carries `launchFeeWindowSeconds: 0`. Older
immutable hooks retain their opening tax; use their retained interfaces and
simulate against their actual hook. A source update does not change deployed pools.

Exact quotes come from simulating the swap. `V4Quoter`, or any `eth_call` of
the swap, runs the launchpad hook and returns what the swap pays. Ascend asks
aggregators to allowlist the hook for simulation quoting.

To quote with pure maths instead:

- The pool key is `currency0` and `currency1` in address order, `fee` the
  dynamic-fee flag (`0x800000`), `tickSpacing` 200 and `hooks` the recorded
  launchpad. The LP fee is 0 on every swap; the hook takes its fee through its
  swap return deltas.
- Read the live fee from `currentFee(poolId)` on the launchpad, in pips. It
  uses the configured fee from launch; there is no additional launch tax.
- On a buy, take the fee off the quote paid in; on a sell, take it off the
  quote paid out (the formulas above). Then use normal v4 pool maths with an LP
  fee of 0.
- Ignore the bid. It fills only exact-input sells, only while the price is
  within 5% of the pool's moving average, and never leaves a seller worse off
  than the pool alone, so a quote without it is conservative.
- Discover pools from the launchpad's `Launched` events and the PoolManager's
  `Initialize` events whose `hooks` is the launchpad. The hook reverts every
  other initialize, every add-liquidity and every donate, so the launch
  position is the pool's whole book.
- Every swap that pays a fee emits the standard `HookSwap` event (URC-2,
  OpenZeppelin `IHookEvents`) with the hook's delta and fee, the fee in the
  quote's slot. The PoolManager's `Swap` carries the AMM's part alone.

## Contract calls

Read current contract state and quote configuration before preparing a call.
Use V4Quoter to simulate swap output, StateView to read pool state and
UniversalRouter for swap execution. Recover each exact pool key from its
PoolManager initialization event: `currency0`, `currency1`, `fee`,
`tickSpacing` and `hooks`. Launch pools use the recorded Ascend launchpad as
their hook; open pools supported by this deployment are hookless. ERC-20 inputs
use Permit2; do not give the router a direct token
allowance in place of Permit2. Keep all amounts in integer base units with the
correct token decimals.

Simulate the exact transaction, including its sender, value, minimum output and
deadline, before requesting a wallet signature. A hash means submitted: wait for
a successful receipt, then read the events and updated balances. Refresh state
and allowances before retrying a failed or declined transaction.

After a timeout or disconnect, check the wallet and chain for the original
submission before retrying. A missing hash does not prove that nothing was
sent. Retry only after confirming non-submission or failure; do not blindly
repeat the send through a different wallet method or transport.

## Public token images and metadata

The app's launch calls name their art: after the call's ABI arguments they carry
`abi.encode(bytes32("ascend.metadata.v1"), "ipfs://<metadata CID>")`, or the
`ascend.disclosure.v1` form with the URI beside an allocation plan. The factories
ignore these bytes. Ascend shows that art once it has seen the launch, when
the call went straight to the factory and Ascend's metadata API holds the URI
for the wallet that sent it: the creator the launch names, or another wallet
while that creator has published no claim. A tag on its own is not
an authenticated claim: bind it to the successful factory call and its receipt.
A published claim's IPFS JSON contains `name`,
`symbol`, `description` and an `image` field holding a second `ipfs://CID`, plus
optional website/social links. Fetch that JSON and its image through your own
IPFS node or gateway. Reading them requires no Ascend account, API key or
signature.

Every launch goes through the escrow factory:
`EscrowFactory.launch(LaunchParams, Route(recipient, dev, builder, cto))` for a
plain launch, and `SeatFactoryV5.launch(LaunchParams, SeatConfigV5, seatSalt, builder)`
for a Holder Rewards or buyback coin, which deploys the seat and launches through
the escrow factory in the same call. `LaunchParams(name, symbol, quote,
devBuyQuote, minDevOut, salt, creator, fee, builderFee)` carries `creator` zero:
the factory makes the pool's new escrow its creator role. The `Route` names who
that escrow pays (`recipient`: a wallet, or the seat), who receives the dev buy
(`dev`), any builder and the takeover opt-in. The launchpad's
`Launched(poolId, token, creator, quote, supplySeeded, devBuyTokens, fee, builderFee)`
names that escrow as `creator`, and the factory's
`EscrowLaunched(poolId, escrow, recipient, dev, builder, ruleSet, cto)` names the
recipient it pays. Decode each contract with the release's ABIs.

Neither factory takes value. The dev buy is an ERC-20 `transferFrom` of
`devBuyQuote` from the caller, so approve the factory you call for that amount
first, holding the quote: for a HYPE pair, wrap HYPE into WHYPE before the
approval. Whatever the dev buy does not spend is refunded in the same call.

`SeatConfigV5(holderBpsQuote, holderBpsToken, buybackBps, burnBought, devBuyTo,
payees, bps)` fixes a seat coin's split for good: the holders' shares, the
buyback's share of every arrival on both sides (`holderBps* + buybackBps` at
most 10,000; the payees split the rest) and, with a buyback share, whether the
bought coins go to the factory's `BurnSink` (`burnBought`) or to the first
payee's wallet. A buyback coin is a seat whose split has a buyback share. Its
buyback buys the coin in its own launch pool, paying the pool's fee,
at most 10% of the buyback pot a call and at least 10 minutes apart, only while
the price is within 5% of the escrow's moving average (`BuybackExecuted`).

Each pool's graduation and forfeits move by public calls on its escrow:
`startHold()`, `breakHold()`, `graduate()`, `drop()`, `forfeit()`, `poke()` and
`settle()`. Anyone may send one; each reverts until its condition holds on
chain, and nothing goes to the caller. A coin above the graduation line also
drops back inside `settle()` once the escrow's moving average is below it.
Ascend's crank sends these calls when they are due.

The escrow's and the seat's own events carry no pool id. Map each escrow to its
pool with `EscrowLaunched` from the recorded escrow factory, and each seat
through the `recipient` that event names; never trust an escrow address
without it.

### Builder links and Permissioned Launches

Builder launches name the builder in the `Route` (or the seat launch's
`builder`), `LaunchParams.builderFee` carries its fee, and the escrow factory
reverts unless `builderFeeCap(builder)` covers it. Builders are approved by
agreement with Ascend; an unapproved address has a cap of zero. The escrow pays
the builder's part off the top of every fee, claimed with `claimBuilder()`.
A builder link opens Ascend's launch form with `?builder=<address>`; the form
charges that builder's approved cap. Adding `custom=1` opens the Permissioned Launch
form and keeps the builder.

A Permissioned Launch is a partner launch the escrow factory's owner approved:
`approvePartner(launcher, PartnerTerms)` fixes, for one launcher and one pool,
the token address (`predictPartnerToken`), the quote and opening tick, a supply
of 1M to 10B whole tokens, a grant of up to 70% of it to `grantRecipient` in the
launch transaction, the escrow's recipient, a partner RuleSet, the takeover
opt-in and an expiry at most 7 days away (`PartnerApproved`). The launcher calls
`EscrowFactory.launchPartner(LaunchParams, builder, expectedTermsHash)` with the
hash it reviewed (`partnerTerms(launcher)`); there is no dev buy, the approval is
used once, and `PartnerLaunched(poolId, launcher, termsHash, grantRecipient,
supply, allocation)` records the pool. `revokePartner` withdraws an approval.

### Read a launch statement from the creation transaction

A launcher may publish an allocation plan with the launch; the Ascend form
requires one above an estimated 10% initial buy on plain launches. It rides
after the factory call's arguments as `abi.encode(bytes32 tag, string metadataUri,
string allocationPlan)`, tagged with the ASCII string `ascend.disclosure.v1`
right-padded to 32 bytes, with the URI empty; the escrow factory and
SeatFactoryV5 ignore trailing calldata. The trimmed plan has 1–500 characters
without control characters (line breaks and tabs are allowed).

1. Find the token's `Launched` log from the verified launchpad in a successful
   receipt, and the factory's `EscrowLaunched` for the same pool.
2. For a direct wallet launch, fetch the transaction and check `to` is the
   escrow factory or SeatFactoryV5. For a smart-wallet launch, use the
   successful inner call to one of them from a transaction trace, matched by
   the token it returned. Never scan unrelated calldata for a tag.
3. Re-encode the decoded call canonically, require it to match the start of the
   input exactly, and decode the remaining bytes as above, with no additional
   bytes.
4. The plan is the launch caller's statement, not a verified commitment or a
   vesting mechanism. These are app policies; the contracts enforce no cap.

Ascend's app emits and checks these bytes with its own `encodeLaunchDataV5` and
`decodeLaunchMetadataV5`, which are not a published package; the check does not
authenticate the transaction or receipt. To publish a plan from another client,
append the suffix to the launch encoding **before** simulating, estimating gas
and signing:

```js
import { concatHex, encodeAbiParameters, encodeFunctionData, stringToHex } from 'viem';

const data = concatHex([
  encodeFunctionData({ abi: escrowFactoryAbi, functionName: 'launch', args: [launchParams, route] }),
  encodeAbiParameters([{ type: 'bytes32' }, { type: 'string' }, { type: 'string' }], [
    stringToHex('ascend.disclosure.v1', { size: 32 }),
    '', // no metadata URI
    allocationPlan, // 1–500 characters, trimmed
  ]),
]);
await publicClient.call({ account, to: escrowFactory, data, value: 0n });
const gas = await publicClient.estimateGas({ account, to: escrowFactory, data, value: 0n });
const hash = await walletClient.sendTransaction({ account, to: escrowFactory, data, gas, value: 0n });
```

### Public lookup by token address

`GET https://api.ascendlaunch.xyz/v1/tokens/<token-address>/metadata`
returns the current published claim on Elysium mainnet (99901), including
`metadataUri`, `imageUri`, `name`, `symbol`, `description`, socials, `creator` and
`verified`. `creator` is the wallet the claim is from (it signed the claim, or sent the launch that named the art): the pool's creator, or
the wallet that launched the pool when its creator has published none. This endpoint already exists, is readable without signatures or
authentication, and sends `Access-Control-Allow-Origin: *`. `404` means there is
no visible claim; retry service failures rather than treating them as missing
art. Claims with `verified: false` remain provisional. Convert `imageUri` using
your IPFS gateway when a direct image URL is needed. Answers come from
Cloudflare's edge: a verified claim can be up to five minutes old and a `404` up
to 30 seconds, so new or replaced art can take that long to appear. Polling
faster returns the same answer.

This lookup is where a launch's art is read: it is attached after the launch, and
a token has no claim until its creator, or the wallet that launched it, publishes
one. A claim can be replaced later, so read it again rather than keeping it for good.

## API access

Public metadata lookup needs no API key or wallet signature. Cache successful
responses, resolve IPFS content through your own gateway, and back off on 429 or
service failures. No general production throughput is promised during early access.

Use the Ascend app to publish artwork. A successful on-chain launch remains
successful if metadata publication needs a retry.

For additional API access, contact [@AscendLaunch](https://x.com/AscendLaunch)
with your team, product, use case, target network and expected request volume.
Integration details and usage limits are coordinated directly with teams.
