signaldocs Open Signal

Build with Signal

Discover v4 pools

A token is a Signal v4 launch when a reviewed Signal factory bore it. A ticker, a picture, an API search hit or a pool that merely contains the token proves nothing.

The birth events

Every launch emits one Launched from its factory. Release 4.2 also emits Details in the same transaction.

solidity
event Launched(
    bytes32 indexed id,          // the Uniswap v4 pool id
    address indexed token,
    address indexed creator,     // the launching wallet
    address quote,               // the pair asset
    bool rewards,                // true: a holders token
    uint16 poolFeeBps,
    uint16 buyTaxBps,
    uint16 sellTaxBps,
    uint16 protocolBps,
    Split split,                 // (creator, holders, liquidity, burn, floor), bps
    uint256 firstBuyTokens,      // tokens the launcher's first buy received, 0 if none
    Blocks blocks,               // the launch's optional rules
    Leg[] legs,                  // named co-recipients of the creator side
    Module module,
    uint128 liqBatchQuote
);

event Details(address indexed token, bytes32 metaHash, string uri);
EventTopic 0
Launched (4.2 and 4.1)0x8fd460cf6081c16be4921e6d01164cb0745944b824325879617eefd6a81b3f7b
Details (4.2)0x4b87591452f11d5711de01c570b9a9519aa41e8dccd0e5878dc6c53c4eeb4514
Launched (release 2)0x8a27775651889aee0c899890c76e303ec5efd6b0a63392cecd6a040572142e56

Release 2's Launched is the same event without the last two fields (module, liqBatchQuote), so its topic differs.

Decode with the factory ABI; the struct layouts are in it. Scan only the factory addresses in the deployment record, from each one's first block. Use bounded block ranges, keep a cursor and backfill what you miss. Some public HyperEVM RPC endpoints return an empty result, not an error, for a log range they consider too wide: page in small ranges and confirm a known launch appears before trusting an empty answer.

The event is a trigger. The authority is the hook's own record, read back at a pinned block.

Another route: the PoolManager

Every pool is created through the PoolManager, so its Initialize log names the pool's hook. Filtering Initialize by hooks in Signal's hook list finds the same pools without a factory ABI. A new Signal hook means a new address to add to that list.

Confirm the identity at one block

  1. factory.poolOf(token) returns a non-zero pool id, and factory.isSignalToken(token) is true.
  2. hook.terms(id).token is the token, and hook.terms(id).quote is the pair asset you expect.
  3. hook.poolKey(id) returns fee = 0, tickSpacing = 200, hooks = that hook, and the two currencies are the token and the pair asset in address order.
  4. keccak256(abi.encode(currency0, currency1, fee, tickSpacing, hooks)) equals the pool id.
  5. factory.hook() is the hook and hook.factory() is the factory.
  6. On release 4.2, the token's code is the EIP-1167 minimal clone of the plain implementation, or of the holders implementation when terms.rewards is true.

The read-only example performs these checks.

Token facts

FactWhere to read it
Supply and decimalstotalSupply() is 1,000,000,000 × 10^18; decimals() is 18
Holders token or plainrewardsEnabled(), or terms.rewards
Pair assetterms.quote; a holders token also exposes rewardAsset()
LauncherLaunched.creator, and hook.creatorOf(id)
Launch timeterms.launchTs (unix seconds)
Start priceterms.startTp, a tick: price in pair-asset units per token = 1.0001^startTp, before decimals

Names can change

A release 4.1 or 4.2 token is a plain ERC-20 with one unusual power: its launcher (identityOwner()) can change the token's name and symbol with setIdentity, at most once every 24 hours. The token emits IdentitySet(string name, string symbol), and bornName() and bornSymbol() keep what it was launched as. The address, the pool and every rule stay the same. Release 2 tokens do not have this: their name and symbol are fixed and these calls revert.

Index a token by address. Refresh the name and ticker on IdentitySet. Treat a ticker match as nothing more than a ticker match.

Words and pictures

contractURI() on a 4.2 token returns https://signal.family/api/meta/0x<token>, an ERC-7572 document: name, symbol, description, image, website, external_link, twitter, telegram. The token stores none of it. The factory's Details event carries the uri the launcher supplied (on current launches a https://signal.family/m/<hash> address) and metaHash = keccak256(bytes(uri)), the hash of that address string. It records which address the token was launched with. It does not commit to the document's contents, which Signal's server supplies.

All of it is display content supplied by a launcher. Never let it choose a spender, an RPC host, a route or a transaction recipient.

API-assisted discovery

ReadUse
/api/born?every=1Every launch, newest first, in pages. A v4 row has launch_venue: "v4" and pad_addr = its factory
/api/token/<address>The token record with v4_launch: pool id, pool key, terms as read at the birth block, and cached hook state
/api/configWhich factory is open, and the admitted pair assets

These are indexed views with their own freshness. See the public data API. Rebuild durable history from chain logs when your product needs completeness.