# Orpheus integration guide

For trading bots, aggregators, wallets and indexers. Orpheus is a fair-launch token launchpad on **Elysium Testnet**.

Site: https://orpheusfun.com · Live version of this guide: https://orpheusfun.com/docs/integrate

## Network

| | |
| --- | --- |
| Chain | Elysium Testnet (Kinetiq's Hyperliquid L2) |
| Chain ID | `99801` |
| Gas token | HYPE (18 decimals) |
| RPC | `https://testnet-rpc.elysium.kinetiq.xyz` |
| Explorer | https://explorer-elysium-testnet.t.conduit.xyz |

## Contracts

| Contract | Address | What it does |
| --- | --- | --- |
| `OrpheusFactory` | `0x8A43708A4CcF846E0805dFE1cd52534d49bd5a72` | Creates tokens, emits `TokenCreated` |
| `BondingCurve` | `0xa76d5C7B5A8B267a3BDCdb9579DE8d473Aa7aaB3` | **Buy/sell for every Orpheus token, before and after it Rises** |
| `RisenPools` | `0xB0be85E0054148562FF4c64aF13B58B6106B60cC` | Uniswap v4 hook + locked liquidity for graduated tokens |
| `PoolManager` (Uniswap v4) | `0x0F22F8eaE22acbCb8AB591eeB71f22E35D81B4C3` | Testnet deployment (Uniswap hasn't deployed v4 to Elysium yet) |

Contracts were deployed at block `17332980`; scan events from there. These are testnet addresses and change on redeploys; mainnet addresses will be published separately.

## Tokens

- Standard ERC-20, 18 decimals, fixed supply of **1,000,000,000**. No transfer taxes, blacklists or pausing. One difference: the `BondingCurve` and `RisenPools` contracts don't need an allowance, which is what lets `sell` work without an approve step; the curve only ever pulls tokens from the seller who calls `sell`.
- Every Orpheus token is created by the factory. To check a token is genuine: `OrpheusFactory.creatorOf(token) != address(0)`.
- Metadata: `TokenCreated.metadataURI` is an `ipfs://` (or `data:`) URI to JSON with `name, symbol, description, image, x, telegram, website`. Resolve `ipfs://` through any gateway, e.g. `https://gateway.pinata.cloud/ipfs/`.

## Trading: one entry point for every token

Use the **BondingCurve** for all trades. While a token is on its curve ("The Underworld") it trades on the curve; after it Rises the same functions route through its Uniswap v4 pool automatically.

```solidity
// Buy: send HYPE as msg.value
function buy(address token, uint256 minTokensOut) payable returns (uint256 tokensOut);

// Sell: no approve needed — Orpheus tokens let the curve pull from the seller
function sell(address token, uint256 tokensIn, uint256 minHypeOut) returns (uint256 hypeOut);

// Quotes (fees included)
function quoteBuy(address token, uint256 hypeIn) view returns (uint256 tokensOut, uint256 feeHype);
function quoteSell(address token, uint256 tokensIn) view returns (uint256 hypeOut, uint256 feeHype);

// State
function curveState(address token) view returns (
    uint256 hypeRaised, uint256 tokensSold, uint256 spotPrice /* HYPE wei per 1e18 tokens */,
    bool graduated /* true = Risen */, uint64 launchedAt /* unix seconds */);
function currentFeeBps(address token) view returns (uint16);   // fee a curve trade pays right now
function creatorFeeBps(address token) view returns (uint16);   // set at launch, 0–800, fixed forever
function graduationThreshold() view returns (uint256);         // HYPE (wei) a curve raises before it Rises
```

**Always pass a real `minTokensOut` / `minHypeOut`** (quote × (1 − slippage)). Trades revert with `Slippage()` otherwise.

### Fees

| Phase | Fee | Taken from |
| --- | --- | --- |
| Curve, first 3 s after launch | **Anti-snipe tax**: 99% at launch, falling each second to the normal fee | HYPE in (buys) / HYPE out (sells) |
| Curve, after that | Creator fee (0–8%, per token) + 2% platform | HYPE side |
| After Rising | **3%** (1% creator, 1% $ORPH stakers, 1% treasury) | HYPE side, taken by the pool hook |

Read `currentFeeBps(token)` before trading a freshly launched token; it's 9900 at the moment of launch.

### Edge cases

- **Buy that fills the curve:** only the HYPE needed to fill it is taken (plus fee); the rest is refunded in the same transaction and the token Rises in that transaction.
- **Price floor after Rising:** the pool's liquidity can absorb every token the curve sold, so market cap can't go below the launch value. A sell that would cross the floor stops at it and the unsold tokens are returned (`hypeOut` may be less than `tokensIn` implies, and some tokens come back).
- **Gas:** Elysium's base fee moves block to block. Set `maxFeePerGas` to about 2× the current base fee or wallets may reject with "max fee per gas less than block base fee".

## Events (for indexers and "new token" feeds)

```solidity
// OrpheusFactory
event TokenCreated(address indexed token, address indexed creator, string name, string symbol, string metadataURI);

// BondingCurve: every trade, curve or pool, routed through Orpheus
event Trade(address indexed token, address indexed trader, bool isBuy, uint256 hypeAmount,
            uint256 tokenAmount, uint256 feeHype, uint256 hypeRaised);
event Graduated(address indexed token, bytes32 pool, uint256 hypeLiquidity);   // token Rose

// RisenPools
event Risen(address indexed token, bytes32 indexed poolId, uint256 hype, uint256 tokens, int24 tickBond, int24 tickFloor);
```

## Trading a Risen token directly on Uniswap v4 (optional)

If you route through Uniswap v4 yourself instead of `BondingCurve.buy/sell`:

| PoolKey field | Value |
| --- | --- |
| `currency0` | `address(0)` (native HYPE) |
| `currency1` | the token |
| `fee` | `0` |
| `tickSpacing` | `1` |
| `hooks` | `RisenPools` (`0xB0be85E0054148562FF4c64aF13B58B6106B60cC`) |

`RisenPools.poolKey(token)` returns it. The hook takes 3% of the HYPE side using return deltas (`beforeSwapReturnDelta` / `afterSwapReturnDelta`), so your router must support hooks that return deltas. Fees from direct swaps accumulate in the hook; anyone can pay them out with `RisenPools.collectFees(token)`. `RisenPools.spotPrice(token)` and `floorPrice(token)` give prices in HYPE wei per 1e18 tokens.

## Example (viem)

```ts
import { parseAbi, parseEther } from "viem";
// publicClient / walletClient: your viem clients for chain 99801; token: the Orpheus token address.

const CURVE = "0xa76d5C7B5A8B267a3BDCdb9579DE8d473Aa7aaB3";
const abi = parseAbi([
  "function buy(address token, uint256 minTokensOut) payable returns (uint256)",
  "function sell(address token, uint256 tokensIn, uint256 minHypeOut) returns (uint256)",
  "function quoteBuy(address token, uint256 hypeIn) view returns (uint256 tokensOut, uint256 feeHype)",
  "function quoteSell(address token, uint256 tokensIn) view returns (uint256 hypeOut, uint256 feeHype)",
]);

const hypeIn = parseEther("0.01");
const [tokensOut] = await publicClient.readContract({ address: CURVE, abi, functionName: "quoteBuy", args: [token, hypeIn] });
await walletClient.writeContract({
  address: CURVE, abi, functionName: "buy",
  args: [token, (tokensOut * 97n) / 100n], // 3% slippage
  value: hypeIn,
});
```

## Contact

Questions or integration help: reach the Orpheus team on X (link in the site footer).
