# For integrators

Launchpad discovery, token metadata, and market data are available through the
Sushi Data API GraphQL endpoint:

```text
https://production.data-gcp.sushi.com/api
```

Every Launchpad query is under `Query.launchpad` and requires an explicit chain
ID. Launchpad V2 is available on Robinhood Chain (`4663`) and is identified by
the `SUSHI_V2` provider. Discovery defaults to Sushi V1 and V2; pass
`providers: [SUSHI_V2]` when an integration only supports the V2 fields and
behavior documented here.

## List tokens

Use `launchpad.tokens` for cursor-paginated discovery. It supports provider,
creator, and text-search filters and can sort by market capitalization,
creation time, TVL, TVL change, or volume.

```graphql
query LaunchpadTokens($input: LaunchpadTokensInput!) {
  launchpad {
    tokens(input: $input) {
      edges {
        cursor
        node {
          chainId
          address
          creator
          provider
          name
          symbol
          decimals
          initialSupply
          initialFdvUsd
          indexingStatus
          createdAt
          metadata {
            description
            links {
              kind
              url
              label
            }
            revision
            updatedAt
          }
          pool {
            address
            feeTier
            quoteToken {
              address
              symbol
              decimals
            }
          }
          metrics {
            priceUsd
            marketCapitalizationUsd
            fullyDilutedValuationUsd
            currentTvlUsd
            volumeUsd {
              h1
              h6
              h12
              h24
            }
            tvlChangePercent {
              h1
              h6
              h12
              h24
            }
            isStale
            asOf
          }
          ... on SushiV2LaunchpadToken {
            launchCreator
            currentSupply
            feeReceiver
            feeReceiverLocked
            liquidityMode
            feeDisposition
            feeSplit {
              sushiFeeBps
              nonSushiFeeBps
            }
            devBuy {
              quoteSpent
              launchTokenReceived
            }
            burns {
              directFeeBurned
              buybackBurned
              protocolBurned
              totalBurned
            }
            poolInitializedAt
          }
        }
      }
      pageInfo {
        endCursor
        hasNextPage
      }
      totalCount
    }
  }
}
```

Example variables:

```json
{
  "input": {
    "chainId": 4663,
    "providers": ["SUSHI_V2"],
    "first": 20,
    "sortBy": "CREATED_AT",
    "sortDirection": "DESC"
  }
}
```

Pass `pageInfo.endCursor` as `after` to request the next page. Cursors are
opaque and should not be parsed. `metrics` is nullable while a launch is
provisional or market data is not yet available.

## Fetch one token and its metadata

Use `launchpad.token` when you already know the token address:

```graphql
query LaunchpadToken($chainId: LaunchpadChainId!, $address: EvmAddress!) {
  launchpad {
    token(chainId: $chainId, address: $address) {
      address
      creator
      provider
      factoryAddress
      name
      symbol
      initialSupply
      initialFdvUsd
      indexingStatus
      metadata {
        description
        links {
          kind
          url
          label
        }
        revision
        updatedAt
      }
      pool {
        address
        feeTier
        quoteToken {
          address
          name
          symbol
          decimals
        }
      }
      ... on SushiV2LaunchpadToken {
        launchCreator
        currentSupply
        feeReceiver
        feeReceiverLocked
        liquidityMode
        feeDisposition
        feeSplit {
          sushiFeeBps
          nonSushiFeeBps
        }
        devBuy {
          quoteSpent
          launchTokenReceived
        }
        burns {
          directFeeBurned
          buybackBurned
          protocolBurned
          totalBurned
        }
        poolInitializedAt
      }
    }
  }
}
```

`LaunchpadToken` is an interface, so V2-only fields require an inline fragment
on `SushiV2LaunchpadToken`. For V2, `creator` is the transferable current
creator; `launchCreator` is the permanent launching address, and `feeReceiver`
is the independent direct-payout address. Fee values are basis points, so
`2000` means 20%.

`currentSupply` reflects supply-reducing burns, while `initialSupply` remains
the launch-time supply. `protocolBurned` is the sum of direct fee burns and
buyback burns; `totalBurned` also captures any other holder-initiated burns.
`devBuy` is nullable when the launch did not include an atomic first purchase.
The deprecated `positions` interface field is an empty list for V2 and should
not be used to reconstruct Moon Mode liquidity.

`metadata` is always present. A token without creator-authored metadata has an
empty link list, a nullable description, and revision `0`.

## Index launches from events

To discover V2 launches directly from Robinhood Chain, index `TokenLaunched`
logs from block `38,395,704` onward. The logs are emitted by the
[Launchpad V2 contract](https://robinscan.io/address/0xF1716eBf85836ffE2985db9A50dd29e5814caBe9)
at `0xF1716eBf85836ffE2985db9A50dd29e5814caBe9`:

```solidity
event TokenLaunched(
    address indexed launchCreator,
    address indexed token,
    address indexed pool,
    address quoteToken,
    uint8 liquidityMode,
    uint8 feeDisposition,
    address initialFeeReceiver,
    uint16 initialSushiFeeBps,
    int24 startTick,
    uint64 poolInitializedAt,
    uint16 observationCardinalityNext,
    string name,
    string symbol
);
```

The contract source declares `liquidityMode` and `feeDisposition` using enum
types, but both are represented as `uint8` in the ABI. The canonical event
signature and topic are:

```text
TokenLaunched(address,address,address,address,uint8,uint8,address,uint16,int24,uint64,uint16,string,string)
0x67d06515f05b601a4cc554da8c9a0f04285b38503d8bb9b49ae6e91d6640a123
```

| Field | Description |
| --- | --- |
| `launchCreator` | Wallet that created the launch. This is permanent launch provenance even if the current creator is transferred later. Indexed. |
| `token` | Address of the newly deployed launch token. Indexed. |
| `pool` | Address of the newly created SushiSwap V3 pool. Indexed. |
| `quoteToken` | Quote token paired with the launch token. |
| `liquidityMode` | Initial liquidity layout: `STANDARD` (`0`) or `MOON` (`1`). |
| `feeDisposition` | Initial non-Sushi fee handling: `DIRECT_PAYOUT` (`0`), `BURN_LAUNCH_TOKEN_FEES` (`1`), or `BUYBACK_AND_BURN` (`2`). |
| `initialFeeReceiver` | Address configured to receive direct-payout fees at launch. |
| `initialSushiFeeBps` | Sushi's initial share of collected fees, in basis points. |
| `startTick` | Economic launch-price tick derived from the starting FDV. The raw V3 pool tick is `startTick` when the launch token is `token0`, and `-startTick` otherwise. |
| `poolInitializedAt` | Pool initialization time as a Unix timestamp in seconds. |
| `observationCardinalityNext` | V3 oracle's next observation capacity at launch time, read after any `BUYBACK_AND_BURN` capacity increase. |
| `name` | Launch token name. |
| `symbol` | Launch token symbol. |

The event signature is topic 0. The first three fields occupy topics 1–3; all
remaining fields are ABI-encoded in the log data. Treat
`(chainId, transactionHash, logIndex)` as the event's stable identifier, persist
the block number and block hash, and account for chain reorganizations before
considering a launch final.

### Related launch events

`TokenLaunched` identifies the launch, but complete transaction indexing also
uses these events from the same proxy address:

* `PositionCreated` is emitted once for Standard Mode and seven times for Moon
  Mode. It contains each position ID, position index, intended FDV boundaries,
  actual ticks, token allocation, token usage, and liquidity. An
  `intendedUpperFdvUsd` value of `0` denotes the maximum usable tick rather than
  a zero-dollar upper bound.
* `InitialBuyExecuted` is emitted when `launchAndBuy` or `launchAndBuyNative`
  completes the optional first purchase. It contains the recipient, quote
  token, exact input, and launch-token output.

Index `CreatorTransferred`, `FeeReceiverUpdated`, `FeeReceiverLocked`,
`FeeDispositionUpdated`, `SushiFeeBpsUpdated`, and `FeesDistributed` when
maintaining V2 state independently. Supply burns are represented by the launch
token's standard ERC-20 `Transfer` events to the zero address.

Alternatively, after discovering a token, use `launchpad.token` to hydrate
current mutable fields such as `creator`, `feeReceiver`, `feeDisposition`, fee
share, burn totals, metadata, and market data. The Data API does not currently
expose V2 position details, so integrations that need those positions must
retain the `PositionCreated` logs.

## Set metadata

Only the token's current creator can set its offchain metadata. An update is a
signed replacement of the complete metadata document, not a patch:

1. Fetch the token's `creator`, `factoryAddress`, and current
   `metadata.revision`.
2. Normalize the complete metadata document.
3. Prepare the optional image and hash its final decoded bytes.
4. Build the EIP-712 `UpdateMetadata` message and have the current creator
   authorize it.
5. Send the document, optional base64 image, deadline, and signature through
   `launchpad.updateMetadata`.

Each successful update increments the revision by one. If the mutation returns
`REVISION_CONFLICT`, refetch the token, rebuild the complete document with the
new revision, and request a new signature.

### Metadata constraints

The Data API applies these rules before verifying the signature:

| Field | Constraints |
| --- | --- |
| `description` | Optional or nullable. Trimmed, with a maximum of 4,000 UTF-8 bytes. An empty value is normalized to `null`. |
| `links` | At most 20 entries. The complete array replaces the previous links. |
| `links[].kind` | Trimmed, 1–32 characters, starts with an ASCII letter, and otherwise contains only letters, numbers, `_`, or `-`. Normalized to lowercase. |
| `links[].url` | Trimmed, valid HTTPS URL, and at most 2,048 characters. Normalized with the standard URL serializer. |
| `links[].label` | Optional or nullable, trimmed, and at most 64 characters. An empty label is omitted. |

Two links cannot have the same normalized `kind` and `url`. Normalize before
signing: the API verifies the signature against its normalized representation,
so a signature over untrimmed, mixed-case, or otherwise noncanonical values
will not match.

### Image constraints

The optional `image` is the token logo. The final payload accepted by the Data
API must meet all of these constraints:

* PNG, JPEG, or WebP, detected from the decoded file bytes rather than the
  filename or declared MIME type;
* non-empty and no larger than 1 MiB (`1,048,576` decoded bytes);
* width and height between 1 and 512 pixels; and
* canonical base64 containing only the file bytes, without a
  `data:image/...;base64,` prefix.

Hash the exact decoded bytes sent in `image` with SHA-256 and use the resulting
32-byte value as `logoHash` in the signed message. If you resize, compress, or
re-encode the image, hash the processed output rather than the source file.

The Sushi frontend accepts PNG, JPEG, or WebP source files up to 20 MiB. When a
source exceeds the API's byte or dimension limits, it scales the image
proportionally to fit within 512×512 and attempts to encode it as WebP at
decreasing quality levels until it is at most 1 MiB. Integrators can implement
the same preprocessing or require users to supply an already-valid image.

Omit `image` and sign the all-zero `bytes32` as `logoHash` to leave the current
logo unchanged. Metadata responses do not expose the logo hash, and the
mutation does not currently provide a logo-removal operation.

### Build the signature

Use this EIP-712 domain:

```ts
const domain = {
  name: 'Sushi Launchpad API',
  version: '1',
  chainId,
  verifyingContract: factoryAddress,
} as const
```

`factoryAddress` must be the historical factory returned for the token, not
necessarily the newest factory deployed on the chain.

The types and message are:

```ts
const types = {
  LaunchpadMetadataLink: [
    { name: 'kind', type: 'string' },
    { name: 'url', type: 'string' },
    { name: 'label', type: 'string' },
  ],
  LaunchpadMetadataDocument: [
    { name: 'description', type: 'string' },
    { name: 'links', type: 'LaunchpadMetadataLink[]' },
  ],
  UpdateMetadata: [
    { name: 'tokenAddress', type: 'address' },
    { name: 'expectedRevision', type: 'uint256' },
    { name: 'metadata', type: 'LaunchpadMetadataDocument' },
    { name: 'logoHash', type: 'bytes32' },
    { name: 'deadline', type: 'uint256' },
  ],
} as const

const typedData = {
  domain,
  types,
  primaryType: 'UpdateMetadata',
  message: {
    tokenAddress,
    expectedRevision: BigInt(metadata.revision),
    metadata: {
      description: normalizedDescription ?? '',
      links: normalizedLinks.map((link) => ({
        kind: link.kind,
        url: link.url,
        label: link.label ?? '',
      })),
    },
    logoHash,
    deadline,
  },
} as const

// EOA example. For a contract wallet, use its typed-data signing flow.
const signature = await walletClient.signTypedData({
  account: creator,
  ...typedData,
})
```

`deadline` is a Unix timestamp in seconds. It must not be expired or more than
15 minutes in the future; the Sushi frontend uses a 10-minute deadline. Sign
`typedData` with the current creator wallet.

For an EOA creator, submit the EIP-712 signature returned by the wallet. For a
contract-wallet creator, obtain the signature bytes through that wallet's
signing flow and submit them unchanged. The signature is an opaque hex value
and does not need to use the 65-byte EOA signature format.

The API reads the current creator from the token's historical factory. If that
address has deployed code, it hashes `typedData` and verifies the supplied
signature against the creator contract using EIP-1271. The contract must
validate the digest of the exact normalized metadata, logo hash, expected
revision, and deadline shown above. A contract signature that does not validate
is rejected with `NOT_CREATOR`.

### Submit the mutation

```graphql
mutation UpdateLaunchpadMetadata(
  $input: LaunchpadUpdateMetadataInput!
  $signature: Bytes!
) {
  launchpad {
    updateMetadata(input: $input, signature: $signature) {
      description
      links {
        kind
        url
        label
      }
      revision
      updatedAt
    }
  }
}
```

Example variables:

```json
{
  "input": {
    "chainId": 4663,
    "tokenAddress": "0x...",
    "expectedRevision": 0,
    "metadata": {
      "description": "A Launchpad token",
      "links": [
        {
          "kind": "homepage",
          "url": "https://example.com/",
          "label": "Website"
        }
      ]
    },
    "image": "<canonical base64 without a data URL prefix>",
    "deadline": "<current Unix time + 600>"
  },
  "signature": "0x..."
}
```

The `deadline` GraphQL value is serialized as a decimal string. Omit `image`
when no logo is being added or replaced. The API verifies the signature against
the canonical onchain creator and performs the metadata and optional logo
update together.

## Token logos

Logo URLs are deterministic and are not returned by the GraphQL metadata
object. Build the URL from the chain ID and lowercase token address:

```text
https://cdn.sushi.com/tokens/{chainId}/{lowercaseTokenAddress}.jpg
```

For example:

```text
https://cdn.sushi.com/tokens/4663/0x1234....jpg
```

The HTTP response is the logo-presence signal:

* A successful `2xx` response means a logo is set.
* A `404` response means no logo is set for that token.

Server-side integrations can issue a `HEAD` request. Browser clients can render
the URL and show a deterministic fallback from the image error handler. Do not
infer logo presence from `metadata.revision`, because creators may save metadata
without uploading a logo and may replace a logo in a later revision.

## V3 pools

Every launch pool is created through the standard SushiSwap V3 factory and uses
the 1% fee tier (`feeTier: 10000`). The Data API returns the pool address and
quote token under `LaunchpadToken.pool`.

Use the [SushiSwap V3 contract list](/contracts/clamm) for factory and position
manager addresses. V2 position NFTs are permanently held by a non-upgradeable
custodian with no path to transfer them or decrease liquidity. The
factory-created pool remains a standard SushiSwap V3 pool and can be queried or
routed like other V3 pools.
