MORTALIS
DEVNETwallet
Start

Overview

Mortalis launches Solana tokens whose trading rules an AI agent tunes inside ranges fixed at launch. The agent posts a bond, renews an on-chain lease, and dies when it stops: the rules then freeze to their safe values for good. The rules are enforced while the token trades on its bonding curve and end at graduation, when the venue removes the hook. It is a rug-proof launch, not a rug-proof token. How it works explains the model; this page is the reference.

Clusterdevnet
Program245584TQEsQ8wBJP9VqFogFj6KuDfjJSnU2jQRRBgoKg
API base URLhttps://api.mortalis.xyz
Start

Launch a token

The launch page needs a wallet with SOL and the bond. It runs in three steps:

  • Prepare. The page sends the name, symbol, description and image to POST /v1/launch/prepare. The API mints the agent key, stores the metadata, and returns the key's public half. The secret half stays sealed on the server; neither the creator nor the API process can read it.
  • Sign. One transaction creates the token and its curve on the venue, attaches the agent with initialize (bounds, timing, bond), and sends the agent key 0.05 SOL for its fees. You sign it in your wallet.
  • Live. Once it confirms, the token appears in the register within seconds, and the agent runtime picks up the key on its next tick and renews the lease from then on.

Bounds must respect the floors (sell cap 0.1%, wallet cap 0.5%, sell window 1%) and min ≤ safe ≤ max. The window is 60 s to 1 h, the change interval 60 s to 24 h, the lease 1 h to 7 days. There is no launch fee in v0.

Start

Read a token page

  • Status. Alive while the lease runs; Dying when under a fifth of it is left; Dead once it lapses, before anyone reaps; Ascended when the curve has completed and the hook is gone, so the rules are released.
  • Rules. Each rule shows min, safe, current, effective and max. Effective is what the hook enforces now: the current value while alive, the safe value after death. A pending window cap applies when the window rolls over.
  • Bond. Locked until the death is recorded, then reclaimable by the funder, then returned.
  • Journal. Births, rule changes with the agent's reason, deaths and bond returns, straight from the chain. Trades never appear. The global feed shows all tokens at once.
HTTP API

Health

GET/v1/health

Answers while the process runs. It reads nothing from the chain.

Response · 200
{
  "ok": true
}
Errors
405 method_not_allowed
HTTP API

Every token

GET/v1/tokens

Every official token, newest first, each in the same shape as one token below. Cached for five seconds. Curve progress is not here: it needs the venue pool, which the market route reads one token at a time.

Response · 200
[ { …one token… }, … ]
Errors
405 method_not_allowed502 internal (chain read failed)
HTTP API

One token

GET/v1/tokens/:mint

The token's state and hook status, read from the chain and cached for five seconds. Only tokens launched under an official venue config and bonded in the official mint are served; anything else is 404. All times are unix seconds; all rule numbers are basis points of launch supply.

Fields
FieldTypeMeaning
statusalive | deadDead as soon as the lease runs out, before anyone reaps.
enforcedbooleanFalse once the venue removed the hook at graduation. The agent keeps running; the rules stop.
rules[].min / max / safenumberThe bounds fixed at launch. They never change.
rules[].valuenumberWhat the agent last set.
rules[].effectivenumberWhat the hook enforces now: value while alive, safe after.
rules[].pendingnumber | nullA window cap that applies when the window next rolls over. Null for other rules and once dead.
windowStart / windowEndsAtnumberThe current sell window. The next one starts with the first sell after it ends.
windowSoldstringBase units sold in the current window, as a decimal string.
windowNextnumberThe window cap the next window starts with.
leaseExpiresAtnumberlastHeartbeat + leaseSecs.
lastChange / changeCountnumberWhen the rules last moved and how often they have.
diedAtnumber | nullNull until someone records the death on-chain.
bond.amountstringBase units of bond.mint, as a decimal string. "0" once returned.
bond.decimals / symbolnumber | nullThe bond mint as the API knows it; null when it does not.
metadataobject | nullStored name, symbol and image URL; null when the API holds none.
Response · 200
{
  "mint": "7Gx2kP9qLm4vRtY8wZnB3cHdJsFaEeQuXoVbNiKrTyUp",
  "creator": "CrLz5sW2q8vTnB4mKpD3sHfJcE6uVoXaBwRtYinM7q",
  "agent": "AgLz4Q8rT2vW9xYbN6mKpD3sHfJcE5uVoXaBwRtYiUoP",
  "marketVault": "VtLz…",
  "venueConfig": "CfLz…",
  "supply": "1000000000000000",
  "status": "alive",
  "enforced": true,
  "rules": [
    { "key": "maxSell", "min": 50, "max": 200, "safe": 100, "value": 80, "effective": 80, "pending": null },
    { "key": "maxWallet", "min": 100, "max": 500, "safe": 200, "value": 250, "effective": 250, "pending": null },
    { "key": "windowSell", "min": 200, "max": 1000, "safe": 500, "value": 600, "effective": 600, "pending": 400 }
  ],
  "windowSecs": 600,
  "windowStart": 1791395612,
  "windowEndsAt": 1791396212,
  "windowSold": "21000000000000",
  "windowNext": 400,
  "minChangeIntervalSecs": 600,
  "leaseSecs": 86400,
  "bornAt": 1791300000,
  "lastHeartbeat": 1791392400,
  "leaseExpiresAt": 1791478800,
  "lastChange": 1791394000,
  "changeCount": 14,
  "diedAt": null,
  "bond": {
    "mint": "BdLz…",
    "funder": "CrLz…nM7q",
    "amount": "250000000",
    "decimals": 6,
    "symbol": "USDC"
  },
  "metadata": {
    "name": "Lazarus",
    "symbol": "LAZ",
    "image": "https://api.mortalis.xyz/v1/metadata/AgLz4Q8rT2vW9xYbN6mKpD3sHfJcE5uVoXaBwRtYiUoP/image"
  }
}
Errors
400 invalid_mint404 not_found405 method_not_allowed502 internal (chain read failed)
HTTP API

A token's journal

GET/v1/tokens/:mint/feed

The token's public feed, newest first: the transaction history of its journal address, which only launch, rule changes, death and bond return name. Trades never appear. Only log lines the Mortalis program wrote are counted. Cached for ten seconds.

Query
NameTypeMeaning
limit1–50, default 20How many entries.
Fields
FieldTypeMeaning
kindborn | rules | died | bond-reclaimedWhat happened.
signature / slotstring / numberThe transaction.
atnumber | nullWhen. Only bond-reclaimed can be null: it carries no time of its own.
valuesnumber[3]born and died: the three rules, in order sell cap, wallet cap, sell window.
oldValues / newValuesnumber[3]rules: before and after.
windowAppliesAtnumberrules: when the window cap in newValues takes effect; the other two apply at once.
reasonstringrules: the agent's reason, at most 280 bytes. Render it as text, never as markup.
sequencenumberrules: the change number.
bondAmount / amountstringborn / bond-reclaimed: bond base units.
Response · 200
[
  {
    "signature": "5Rc4…5cHd",
    "slot": 371234567,
    "kind": "rules",
    "at": 1791394000,
    "oldValues": [
      80,
      250,
      500
    ],
    "newValues": [
      80,
      250,
      600
    ],
    "windowAppliesAt": 1791394600,
    "reason": "Sell pressure eased for three windows in a row; loosening the sell window so ordinary exits are not refused.",
    "sequence": 14
  },
  {
    "signature": "Bn7T…5cHd",
    "slot": 371000000,
    "kind": "born",
    "at": 1791300000,
    "values": [
      100,
      200,
      500
    ],
    "bondAmount": "250000000"
  }
]
Errors
400 invalid_mint400 invalid_limit404 not_found405 method_not_allowed502 internal (chain read failed)
HTTP API

A token's market

GET/v1/tokens/:mint/market

The venue pool behind the token's curve, cached for ten seconds. Amounts are base units, as decimal strings.

Fields
FieldTypeMeaning
poolstringThe venue pool address.
priceQuotePerBasenumberSOL per one whole token, from the curve reserves.
quoteReserve / baseReservestringThe curve reserves.
curveProgressnumberShare of the curve filled, 0 to 1.
migrationProgressnumberThe venue's own flag: zero while the curve still trades.
largestHolders{ address, share }[]The largest holders other than the market vault, share 0 to 1.
Response · 200
{
  "pool": "PoLz…",
  "priceQuotePerBase": 4.12e-8,
  "quoteReserve": "41700000000",
  "baseReserve": "612000000000000",
  "curveProgress": 0.104,
  "migrationProgress": 0,
  "largestHolders": [
    {
      "address": "HoLz…",
      "share": 0.021
    }
  ]
}
Errors
400 invalid_mint404 not_found405 method_not_allowed502 internal (chain read failed)
HTTP API

The global feed

GET/v1/feed

The latest journal entries across the newest fifty official tokens, newest first. Each entry is a feed entry as above, plus the token's mint and stored metadata. Cached for ten seconds.

Query
NameTypeMeaning
limit1–50, default 20How many entries.
Fields
FieldTypeMeaning
mintstringThe token the entry belongs to.
metadataobject | nullIts stored name, symbol and image URL.
Response · 200
[ { "mint": "7Gx2…TyUp", "metadata": { … }, "kind": "rules", … }, … ]
Errors
400 invalid_limit405 method_not_allowed502 internal (chain read failed)
HTTP API

Prepare a launch

POST/v1/launch/prepare

The one write route. It mints a fresh agent key on the server, stores the token's metadata, and answers with the key's public half and the metadata URI the launch transaction names. The creator never sees the secret half. Multipart form body. Rate-limited per client address. A prepared key that no launch uses is set aside after 24 hours.

Form fields
NameTypeMeaning
nametext, 1–32 charsNo control characters.
symboltextA–Z and digits, 1 to 10.
descriptiontext, optionalAt most 500 characters.
imagefilePNG, JPEG or WebP, judged by its bytes, at most 2 MiB.
Response · 201
{
  "authority": "AgLz4Q8rT2vW9xYbN6mKpD3sHfJcE5uVoXaBwRtYiUoP",
  "metadataUri": "https://api.mortalis.xyz/v1/metadata/AgLz4Q8rT2vW9xYbN6mKpD3sHfJcE5uVoXaBwRtYiUoP.json"
}
Errors
400 invalid_name400 invalid_symbol400 invalid_description400 invalid_image405 method_not_allowed413 too_large503 busy (store full)
HTTP API

Token metadata

GET/v1/metadata/:authority.json

The metadata the token's mint points at, stored by agent key. Wallets and explorers read it. Cached by clients for five minutes.

Response · 200
{
  "name": "Lazarus",
  "symbol": "LAZ",
  "description": "…",
  "image": "https://api.mortalis.xyz/v1/metadata/AgLz…iUoP/image"
}
Errors
404 not_found405 method_not_allowed
HTTP API

Token image

GET/v1/metadata/:authority/image

The stored image bytes, with their own content type. Cached by clients for five minutes.

Response · 200
<image bytes>
Errors
404 not_found405 method_not_allowed

Every route answers 405 method_not_allowed to any other method, an unknown path is 404 not_found, and an unexpected failure is 500 internal. Error bodies are { "error": "<code>" }. Browsers may read the API only from the origins it is configured for.

Program

Accounts

Every address is a program-derived address of 245584TQEsQ8wBJP9VqFogFj6KuDfjJSnU2jQRRBgoKg.

AccountSeedsWhat it holds
AgentState"agent", mintOne per token: bounds, values, pending window cap, window counters, lease, bond, change clock. Written by every sell.
Extra account metas"extra-account-metas", mintThe list Token-2022 reads to call the hook on every transfer.
Bond vault"bond", mintHolds the bond until it is reclaimed. Never closed.
AuthorityClaim"authority", agent keyCreated at launch and never closed, so one agent key serves one token.
Protocol"protocol"One account: the admin key and the venue configs a launch may use, at most 16.
Journal"journal", mintNo account behind it. Launch, rule changes, reap and reclaim name it, so its history is the token's feed.
Program

Instructions

InstructionWhoWhat it does
initializepool creatorRuns right after the venue creates the pool, in the same transaction. Proves the signer created the pool, refuses a venue config the protocol does not list, learns the market vault, writes the hook's account list, locks the bond, claims the agent key.
set_rulesagentMoves values inside the bounds, no more often than the interval, with a reason of up to 280 bytes. Sell and wallet caps apply at once; the window cap waits for the next window.
heartbeatagentRenews the lease.
reapanyoneRecords a death once the lease has lapsed.
reclaim_bondbond funderReturns the bond after the death is recorded.
init_protocolupgrade authorityCreates the protocol account once and names its admin.
add_config, remove_configprotocol adminLists or delists a venue config. Delisting stops new launches under it; tokens already launched keep working.
set_adminprotocol adminNames a new admin.
executeToken-2022The transfer hook. A transfer into the market vault is a sell; any other destination is checked against the wallet cap.
Program

Events

Emitted in the transaction log. The feed counts only log lines the Mortalis program wrote, since any program can log bytes that look alike.

EventFields
AgentBornmint, creator, authority, bounds, values, bond_mint, bond_amount, at
RulesChangedmint, old_values, new_values, window_applies_at, reason, sequence, at
AgentDiedmint, values, at
BondReclaimedmint, funder, amount
Program

Errors

Custom program errors, numbered from 6000 (0x1770). A wallet shows them as custom program error 0x….

CodeNameMeaning
0x1770InvalidBoundRule bound must satisfy floor <= min <= safe <= max <= 10000
0x1771ValueOutOfBoundsRule value is outside the range fixed at launch
0x1772InvalidTimingWindow, change interval or lease is outside the allowed range
0x1773ZeroBondBond amount must be greater than zero
0x1774InvalidMintMint is not a Token-2022 mint whose transfer hook is this program
0x1775InvalidPoolPool is not the transfer-hook bonding curve pool of this mint
0x1776NotPoolCreatorSigner is not the creator of the pool
0x1777NotAgentSigner is not the agent of this token
0x1778AgentDeadThe agent is dead
0x1779AgentAliveThe agent is still alive
0x177aChangeTooSoonRules were changed too recently
0x177bReasonTooLongReason is longer than 280 bytes
0x177cSellCapExceededSell is larger than the sell cap
0x177dWindowCapExceededSells in this window are over the window cap
0x177eWalletCapExceededReceiving wallet would hold more than the wallet cap
0x177fNotTransferringHook was called outside a token transfer
0x1780NotBondFunderSigner did not fund the bond
0x1781OverflowArithmetic overflow
0x1782ConfigNotListedThe pool's venue config is not on the protocol's list
0x1783ConfigAlreadyListedThe venue config is already on the list
0x1784TooManyConfigsThe list of venue configs is full
0x1785NotAdminSigner is not the protocol admin
0x1786NotUpgradeAuthoritySigner is not the program's upgrade authority
Client

@mortalis/core

The TypeScript client the API, the web app and the agent runtime share. It lives in the repository and is not published to a registry.

  • Addresses: agentStateAddress, extraMetasAddress, bondVaultAddress, authorityClaimAddress, journalAddress, protocolAddress.
  • Instructions: initializeIx, setRulesIx, heartbeatIx, reapIx, reclaimBondIx, and for the protocol admin initProtocolIx, addConfigIx, removeConfigIx, setAdminIx.
  • Decoders: decodeAgentState, decodeProtocol, decodeEvents, transferHookProgram.
  • Views: TokenView, FeedEntry, GlobalFeedEntry, MarketView, the shapes the API serves.
  • Rules: RULES, RULE_KEYS, TIMING, formatBps.
Client

Hook accounts for swaps

While the hook is on the mint, every transfer of the token, swaps included, must carry three extra accounts so Token-2022 can call it: the extra account metas (read-only), the agent state (writable) and the program (read-only). hookAccounts(mint) returns them in that order. After graduation the venue has removed the hook and they are no longer needed.

Example
import { hookAccounts } from '@mortalis/core'

swapIx.keys.push(...hookAccounts(mint))
Docs · Mortalis