# Empire Builder Docs


# Introduction

Empire Builder is your community-rewards and token utility command center.&#x20;

Built on Base and Arbitrum, Empire Builder provides token creators, founders, and builders with the tools they need to build incentive-based, on-chain communities.

Whether you're launching a meme coin, community token, DeFi utility, or artistic hub, Empire Builder lets you:

* Create your own Empire tied to any ERC-20 token, Farcaster profile, or custom hub
* Set up self-custody treasuries
* Launch leaderboards that reward active users
* Use booster mechanics to deepen engagement
* Integrate with Empire partner ecosystems

From dream to empire.


# Overview

Empire Builder is a modular framework that serves as the community layer for token-based projects and influence-based creators, enabling them to build full-fledged utility ecosystems.

Key Concepts:

* Empires: Utility hubs configured for engagement and rewards
* Treasury: The pool of assets used for community reward distribution
* Leaderboards: Composable scoring systems to measure contribution
* Boosters: Multiplier mechanics based on holding patterns (tokens, NFTs, staking)
* Integrations: Tools and APIs to connect Empire logic to other protocols

<br>


# Founders & Vision

Empire Builder is led by:

* DiviFlyy (Adrian): Developer and onchain ecosystem architect
* Yerbearserker (Jordan Oram): Storyteller and systems designer

Together, they’ve built a no-code framework to empower creators and communities with scalable infrastructure for ecosystem coordination.

Vision: A world where tokens are not just assets, but engines of aligned coordination across communities.


# How Empires Work

An Empire is a user-configured instance that wraps around an ERC-20 token, adding:

* Treasury: Funds distributed as rewards
* Leaderboard: Ranking of wallet activity
* Booster Rules: Multipliers for engagement
* Reward Schedules: Frequency & type of reward events

Each Empire has one or more Guardians who manage its configuration, treasury, and updates.

Empires can be public or private, branded or generic, integrated or stand-alone.


# Leaderboards

### What they are for

A leaderboard is how your empire **sees who matters** for rewards and recognition. It ranks people using a metric you choose—token balances, NFT holdings, social activity, a spreadsheet you upload, or scores from your own systems.&#x20;

That ranking becomes the basis for **fair splits** when you send funds from the treasury, and it helps the community understand who is most engaged.

You can run **several leaderboards at once.** Each one appears as its own tab, up to the number your empire supports.

### Ways you can rank people

Roughly, leaderboards fall into a few families:

* **Token holders** — Rank by how much of a chosen ERC-20 people hold.
* **NFT holders** — Rank by how many NFTs people hold from a collection (or related rules the product supports).
* **Social and ecosystem** — Rank using activity or reputation tied to Farcaster or partner signals (channels, casts, interactions, and similar).
* **Your own data** — Rank using scores from an **API you control**, or from a **CSV file** you upload.

The exact options available in the product may grow over time; when you set up a leaderboard, you pick the type that matches how you want to reward behavior.

### What each row means

For each person on a leaderboard you typically see:

* **Score** — The raw number from the metric (for example, token balance or engagement count). This is the “honest” ranking signal before any bonuses.
* **Points** — The score **after boosters** (loyalty multipliers) are applied. Weighted and raffle-style treasury sends use **points** so active supporters can earn a larger share without hiding the underlying score.
* **Total rewards** — How much value this address has already received from **treasury distributions** for this empire—useful for transparency and storytelling.
* **Rank** — Position on this leaderboard (1 is the top).
* **Identity** — When available, a linked social handle (for example Farcaster) so members recognize each other.

### Tabs and ordering

Leaderboards share a fixed set of numbered **slots** (tabs). New leaderboards take the **next free slot** so you do not have to micromanage numbering; the product assigns the lowest unused slot that fits your plan.

### Keeping data fresh

Leaderboards can be **refreshed** so rankings reflect the latest balances or external scores. Refreshes are intentional (not necessarily live every second), and there is a short **cooldown** between refreshes so updates stay predictable and fair for everyone.

### Connecting leaderboards to payouts

When you run a **treasury distribution**, you choose **which leaderboard** drives the payout. You can split funds **evenly** across everyone listed, **in proportion to points** (weighted), or run a **raffle** weighted by points—depending on what fits your community norms.

For how **points** relate to **scores** when boosters apply, see Boosters.


# Boosters

### What they are for

**Boosters** let you **reward loyalty and alignment** beyond raw leaderboard scores.&#x20;

They multiply a member’s leaderboard **points** when they meet extra conditions—holding a specific NFT, holding a partner token, or hitting a reputation threshold, depending on what you configure.

The underlying **score** on the leaderboard stays honest (for example “how many tokens they hold”). Boosters adjust **points** so people who support your broader ecosystem get a **larger share** without rewriting the base ranking logic.

### How they stack

If someone qualifies for several boosters at once, the boosters will **stack**. For example, one booster might provide a 2x boost, another a 4x boost; together, they multiply the score by 6x.

### Who configures them

Boosters are set at the **empire** level by someone your empire designates as a **guardian** (or equivalent role in the product). They generally apply to **all** leaderboards for that empire unless a particular leaderboard is created **without** boosters, for cases where you want a “pure” raw ranking.

### When points update

When a leaderboard **refreshes**, the system re-checks who qualifies for which boosters and recomputes **points** from each person’s **score**. That keeps boosts aligned with current holdings and rules.


# Treasury

### What it is

Every empire has a **single treasury,** a dedicated, audited smart account that holds funds your community shares.&#x20;

Only the **empire guardians** control it. Think of it as the **community chest**: trading fees, allocations from launch, or deposits you choose can land here, and you distribute from here to leaderboard members according to your rules.

The treasury is built so **you keep custody**: the platform does not hold your keys.

### Why “one treasury” across networks

Empires often touch **more than one blockchain** (for example Base and Arbitrum). Your treasury is designed so it has a **consistent identity** across those networks—same conceptual “vault,” recognizable addresses—while still respecting each chain’s assets. That makes accounting and distributions easier for communities that bridge activity across ecosystems.

### What it can hold

The treasury can receive common assets such as **ETH** and **ERC-20 tokens**. Certain widely used tokens may be **pre-authorized** for smoother distributions so routine payouts do not require extra setup each time.


# Integration Patterns

Empire Builder is highly modular. Projects can integrate in several ways:

* Direct: Launch an Empire using your token and existing community
* Partner Protocols: Distribute incentives to/from other platforms
* White Label: Use Empire infrastructure under your own brand
* API Access: Pull leaderboard/rank/reward data into your dApps

Popular integrations include content reward systems, DeFi LP incentives, creator dashboards, and onchain games.


# Example - BizarreBeasts ($BB)

#### Main $BB Empire Overview

* [Discover the BizarreBeasts Empire](https://paragraph.com/@bizarrebeasts/discover-the-bizarrebeasts-empire-everything-you-need-to-know-to-get-started)

#### Supporting Articles

* [Unleashing Synergies: Clank, GigBot, Glanker = Bizarre Growth](https://paragraph.com/@bizarrebeasts/unleashing-synergies-productclank-gigbot-glanker-=-bizarre-growth)
* [Unleash the $BEAST](https://paragraph.com/@bizarrebeasts/unleash-the-dollarbeast)
* [The Bizarre Logic of Burning Tokens](https://paragraph.com/@bizarrebeasts/the-bizarre-logic-of-burning-tokens-scarcity-value-and-growth)
* [Miniapp Dashboard Overview](https://paragraph.com/@bizarrebeasts/bizarrebeasts-miniapp-your-bizarre-dashboard-has-arrived)
* [Beginner’s Guide to BizarreBeasts](https://paragraph.com/@bizarrebeasts/curious-about-crypto-dive-into-bizarrebeasts-a-beginners-guide)
* [Experimenting with ContentCoins](https://paragraph.com/@bizarrebeasts/experimenting-with-contentcoins)
* [Introducing $BIZARRE](https://paragraph.com/@bizarrebeasts/introducing-dollarbizarre)


# Use Cases

Some example verticals using Empire Builder:

* Gaming: Rank in-game actions and reward players
* Social Tokens: Distribute value to the most engaged users
* DeFi Protocols: Rank LPs or stakers and reward participation
* Creator Communities: Incentivize content sharing and curation
* Meme Coins: Add utility to high-volume token projects


# Ecosystem Participation

Empire Builder is a permissionless platform that empowers anyone to build on top of its infrastructure.

Ways to engage with the ecosystem:

* Launch your own Empire for an ERC-20 token, Farcaster profile, or your own custom hub
* Build integrations or custom experiences using leaderboard and booster logic
* Create content, tutorials, or use cases to grow your community
* Participate in community discussions and feedback

This GitBook is maintained by the core team, with contributions from ecosystem partners like BizarreBeast, whose series of real-world use case articles, including Empire treasury rollouts, cross-token collabs, leaderboard competitions, and art-based incentives, help showcase the platform's versatility in creative and community-driven deployments.

For questions or collaboration opportunities, contact the team via[ @glankerempire](https://warpcast.com/glankerempire) or visit[ empirebuilder.world](https://empirebuilder.world).


# API

Empire Builder exposes HTTPS JSON endpoints for listing empires, leaderboards, boosters, rewards, treasury operations, deploy flows, and more.

### Site & base URL

* **Web:** <https://www.empirebuilder.world>

Use the **same origin** for API calls:

```txt
https://www.empirebuilder.world/api/…
```

Example:

```bash
curl -s "https://www.empirebuilder.world/api/top-empires"
```

(Some environments also resolve the apex hostname `empirebuilder.world`; prefer **`www`** for consistency unless your integration relies on another host.)


# Public


# Get Top Empires

Retrieves a paginated list of top Empire tokens.

Retrieves a paginated list of top empires ranked by their computed rank score. Only empires with at least one USD distribution are included.

```
GET /api/top-empires
```

***

### Query Parameters

| Parameter | Type   | Default | Description      |
| --------- | ------ | ------- | ---------------- |
| `page`    | number | `1`     | Page number      |
| `limit`   | number | `20`    | Results per page |

***

### Example

```bash
curl "https://empirebuilder.world/api/top-empires?page=1&limit=20"
```

***

### Response

```json
{
  "empires": [
    {
      "base_token": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b",
      "token_name": "glonkybot",
      "token_symbol": "GLANKER",
      "total_distributed": 1647,
      "total_burned": 251059054,
      "logo_uri": "https://www.empirebuilder.world/EBblackjpg2.jpg"
    }
  ],
  "totalCount": 142,
  "queryTime": 12.4,
  "page": 1,
  "itemsPerPage": 20
}
```

***

### Response Fields

| Field               | Description                                |
| ------------------- | ------------------------------------------ |
| `base_token`        | Token contract address                     |
| `token_name`        | Empire / token name                        |
| `token_symbol`      | Token ticker                               |
| `total_distributed` | Cumulative USD distributed to holders      |
| `total_burned`      | Cumulative tokens burned (raw units)       |
| `logo_uri`          | Token logo URL                             |
| `totalCount`        | Total number of qualifying empires         |
| `queryTime`         | Server-side query duration in milliseconds |
| `page`              | Current page                               |
| `itemsPerPage`      | Results per page                           |

***

### Notes

* Empires with `total_distributed = 0` are excluded.
* Ordered by computed `rank` descending, then `total_distributed` descending as a tiebreaker.
* Empire ranks are recomputed at most once per 24 hours based on market cap and distribution activity.
* Hidden empires are excluded.


# Get Boosters By Empire

Retrieves the list of boosters configured for a specific Empire.

```
GET /api/boosters/[empire_id]
```

***

### Path Parameters

| Parameter   | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| `empire_id` | **Empire ID** (**`base_token`** — see **Empire ID** section below) |

***

### Example

```bash
curl "https://empirebuilder.world/api/boosters/0xYourTokenAddress"
```

***

### Response

```json
{
  "boosters": [
    {
      "id": "uuid",
      "type": "NFT",
      "contractAddress": "0xNFTContract",
      "multiplier": 1.5,
      "requirement": {
        "minAmount": "1"
      },
      "token_symbol": "THING",
      "token_image_url": "https://...",
      "nft_platform": "manifold",
      "nft_standard": "ERC721",
      "chainId": 8453
    },
    {
      "id": "uuid",
      "type": "ERC20",
      "contractAddress": "0xERC20Contract",
      "multiplier": 1.2,
      "requirement": {
        "minAmount": "1000"
      },
      "token_symbol": "DEGEN",
      "token_image_url": "https://...",
      "chainId": 8453
    }
  ]
}
```

***

### Booster Fields

| Field                   | Description                                            |
| ----------------------- | ------------------------------------------------------ |
| `id`                    | Booster UUID (used to remove it)                       |
| `type`                  | `NFT`, `ERC20`, or `QUOTIENT`                          |
| `contractAddress`       | Contract to check holdings against                     |
| `multiplier`            | Score multiplier applied to qualifying addresses       |
| `requirement.minAmount` | Minimum balance / count / score required               |
| `token_symbol`          | Symbol of the booster token or NFT                     |
| `nft_platform`          | NFT platform (e.g. `manifold`, `zora`) — NFT type only |
| `nft_standard`          | `ERC721` or `ERC1155` — NFT type only                  |
| `chainId`               | Chain where the contract lives                         |

***

### Empire ID (`[empire_id]`)

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Get Distribution Records

Retrieves cumulative distribution totals per recipient for an empire

Useful for displaying lifetime earned amounts without scanning every transaction.

```
GET /api/distribution-records/[empireAddress]
```

***

### Path Parameters

| Parameter       | Description             |
| --------------- | ----------------------- |
| `empireAddress` | Empire treasury address |

***

### Example

```bash
curl "https://empirebuilder.world/api/distribution-records/0xEmpireAddress"
```

***

### Response

```json
{
  "success": true,
  "records": {
    "0xRecipientAddress": {
      "totalReceived": 500,
      "timestamp": "2024-06-01T12:00:00Z"
    },
    "0xAnotherAddress": {
      "totalReceived": 125,
      "timestamp": "2024-06-01T12:00:00Z"
    }
  }
}
```

***

### Response Fields

| Field                            | Description                                                 |
| -------------------------------- | ----------------------------------------------------------- |
| `records`                        | Map of recipient address → distribution record              |
| `records[address].totalReceived` | Total USD received by this address across all distributions |
| `records[address].timestamp`     | Timestamp of the last update for this record                |


# Get Empire Rewards

Retrieves reward history (distributions, burns, airdrops) for an empire.

### Get Reward Summary

```
GET /api/empire-rewards/[empire_id]
```

Returns summarized reward history grouped by type. Returns the 3 most recent of each type.

```bash
curl "https://empirebuilder.world/api/empire-rewards/0x..."
```

**Response**

```json
{
  "empire_rewards": [
    {
      "amount": "$1200.50",
      "type": "distribute",
      "created_at": "2024-06-01T12:00:00Z",
      "transaction_hash": "0x..."
    }
  ],
  "burned": [
    {
      "amount": "$300.00",
      "type": "burned",
      "created_at": "2024-05-15T08:00:00Z",
      "transaction_hash": "0x..."
    }
  ],
  "airdrops": [
    {
      "amount": "$50.00",
      "type": "airdrop",
      "created_at": "2024-04-10T10:00:00Z",
      "transaction_hash": "0x..."
    }
  ]
}
```

***

### Get Rewards by Type

```
GET /api/empire-rewards/[empire_id]/[type]
```

Returns the full reward list for a specific type.

| Parameter   | Description                                                                                                   |
| ----------- | ------------------------------------------------------------------------------------------------------------- |
| `empire_id` | **`base_token`** / **Empire ID** (see **Empire ID** section below). Route folder in code is `[tokenAddress]`. |
| `type`      | One of: `distribute`, `burned`, `airdrop`                                                                     |

```bash
curl "https://empirebuilder.world/api/empire-rewards/0x.../distribute"
```

**Response**

```json
{
  "rewards": [
    {
      "id": "uuid",
      "base_token": "0x...",
      "type": "distribute",
      "total_amount": "1200.50",
      "transaction_hash": "0x...",
      "created_at": "2024-06-01T12:00:00Z"
    }
  ],
  "count": 1
}
```

***

### Get Distribution Recipients

```
GET /api/rewards/recipients/[transactionHash]
```

Returns every recipient address and USD amount for a single distribution transaction.

```bash
curl "https://empirebuilder.world/api/rewards/recipients/0xabc123..."
```

**Response**

```json
{
  "recipients": [
    {
      "user_address": "0x...",
      "farcaster_username": "alice",
      "amount": "100.00"
    }
  ],
  "count": 42
}
```

***

### Empire ID (`[empire_id]`)

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Get Empires

Retrieves a paginated list of Empires with different filtering options.

### Get All Empires

```
GET /api/empires
```

### Query Parameters

| Parameter | Type   | Default | Description                                                                                  |
| --------- | ------ | ------- | -------------------------------------------------------------------------------------------- |
| `type`    | string | `top`   | `top` (by rank + distribution), `native` (first-party empires only), `recent` (newest first) |
| `page`    | number | `1`     | Page number                                                                                  |
| `limit`   | number | `7`     | Results per page                                                                             |

### Examples

```bash
# Top empires
curl "https://empirebuilder.world/api/empires?type=top&page=1&limit=10"

# Native empires only
curl "https://empirebuilder.world/api/empires?type=native&limit=20"

# Most recently created
curl "https://empirebuilder.world/api/empires?type=recent"
```

### Response

```json
{
  "empires": [
    {
      "empire_address": "0x...",
      "base_token": "0x...",
      "name": "My Empire",
      "token_symbol": "EMP",
      "owner": "0x...",
      "rank": 95,
      "total_distributed": "1500000",
      "native": "yes",
      "farcaster_name": "myempire",
      "created_at": "2024-01-01T00:00:00Z"
    }
  ],
  "totalCount": 142,
  "page": 1,
  "itemsPerPage": 10,
  "queryTime": 12.4
}
```

***

### Get Single Empire

```
GET /api/empires/[empire_id]
```

Fetch one empire by **`[empire_id]`** (**`base_token`**). Shapes vary — **see Empire ID at the end of this page.**

```bash
curl "https://empirebuilder.world/api/empires/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
```

**Response**

```json
{
  "empire": {
    "empire_address": "0x...",
    "base_token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "name": "USDC Empire",
    "token_symbol": "USDC",
    "owner": "0x...",
    "rank": 88,
    "total_distributed": "500000",
    "total_burned": "10000",
    "native": "no",
    "farcaster_name": "usdcempire",
    "created_at": "2024-03-15T00:00:00Z"
  }
}
```

***

### Search Empires

```
GET /api/empires/search
```

Case-insensitive substring search across empire name, token symbol, Farcaster name, **empire ID** (`base_token`, including tokenless shapes like `fid…`), and exact ERC‑20 address when applicable.

| Parameter        | Type   | Description                     |
| ---------------- | ------ | ------------------------------- |
| `q`              | string | Search query                    |
| `farcaster_name` | string | Exact Farcaster name lookup     |
| `page`           | number | Page number (default `1`)       |
| `limit`          | number | Results per page (default `10`) |

```bash
curl "https://empirebuilder.world/api/empires/search?q=degen&limit=5"
```

***

### Get Empires by Owner

```
GET /api/empires/owner/[wallet_address]
```

Returns all empires owned by a **`0x` wallet address** — this segment is **not** an empire ID; empire id formats → **Empire ID** at the end of this page.

```bash
curl "https://empirebuilder.world/api/empires/owner/0xYourWalletAddress"
```

***

### Empire ID (`[empire_id]`)

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Get Leaderboard By Empire

Retrieves all addresses on a specific Empire leaderboard

```
GET /api/leaderboards/[leaderboardId]
```

***

### Path Parameters

| Parameter       | Description             |
| --------------- | ----------------------- |
| `leaderboardId` | UUID of the leaderboard |

To find leaderboard IDs for an empire, call `GET /api/leaderboards?tokenAddress=[empire_id]` ( **`tokenAddress`** is **Empire ID** — see bottom of page).

***

### Example

```bash
curl "https://empirebuilder.world/api/leaderboards/uuid-here"
```

***

### Response

```json
{
  "success": true,
  "leaderboard": {
    "id": "uuid",
    "empire_address": "0x...",
    "leaderboard_type": "tokenHolders",
    "name": "Top Holders",
    "leaderboard_number": 1
  },
  "entries": [
    {
      "address": "0xAlice...",
      "rank": 1,
      "score": 50000.0,
      "points": 90000,
      "farcaster_username": "alice",
      "totalRewards": 1200.50
    },
    {
      "address": "0xBob...",
      "rank": 2,
      "score": 30000.0,
      "points": 45000,
      "farcaster_username": "bob",
      "totalRewards": 800.00
    }
  ]
}
```

***

### Entry Fields

| Field                | Description                                                                 |
| -------------------- | --------------------------------------------------------------------------- |
| `address`            | Wallet address                                                              |
| `rank`               | Position in leaderboard (1 = top)                                           |
| `score`              | Raw metric (token balance, NFT count, engagement count, etc.) — not boosted |
| `points`             | Boost-adjusted score used for weighted distribution weighting               |
| `farcaster_username` | Farcaster handle, if resolved                                               |
| `totalRewards`       | Lifetime USD received from this empire's distributions                      |

***

### Notes

* Blocked addresses are automatically excluded from entries.
* `points` reflects all active boosters applied to the leaderboard.

***

### List All Leaderboards for an Empire

```
GET /api/leaderboards?tokenAddress=[empire_id]
```

Returns all leaderboards in **slots 1–20** for an empire. Pinned leaderboards appear first.

```bash
curl "https://empirebuilder.world/api/leaderboards?tokenAddress=0x..."
```

**Response**

```json
{
  "success": true,
  "leaderboards": [
    {
      "id": "uuid",
      "empire_address": "0x...",
      "leaderboard_number": 1,
      "leaderboard_type": "tokenHolders",
      "name": "Top Holders",
      "description": "Ranked by EMP token balance",
      "pinned": false,
      "created_at": "2024-04-01T00:00:00Z"
    }
  ],
  "count": 3
}
```

***

### Empire ID (`[empire_id]`)

Query **`tokenAddress`** (`list`, `consolidated`) identifies the empire: same **`[empire_id]`** / **`base_token`** as elsewhere.

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Get Leaderboard Stats for Single Address within Empire

Retrieve the leaderboard score, boosters, and ranking for a single address within a specific Empire.

```
GET /api/leaderboards/[leaderboardId]/address/[walletAddress]
```

***

### Path Parameters

| Parameter       | Description               |
| --------------- | ------------------------- |
| `leaderboardId` | UUID of the leaderboard   |
| `walletAddress` | Wallet address to look up |

***

### Example

```bash
curl "https://empirebuilder.world/api/leaderboards/uuid-here/address/0xYourWallet"
```

***

### Response

```json
{
  "success": true,
  "entry": {
    "address": "0xYourWallet",
    "rank": 12,
    "score": 15000.0,
    "points": 22500,
    "farcaster_username": "alice",
    "totalRewards": 450.25
  },
  "boosters": [
    {
      "type": "NFT",
      "contractAddress": "0xNFTContract",
      "multiplier": 1.5,
      "qualified": true,
      "requirement": { "minAmount": "1" }
    }
  ],
  "leaderboard": {
    "id": "uuid",
    "name": "Top Holders",
    "leaderboard_type": "tokenHolders",
    "leaderboard_number": 1
  }
}
```

***

### Response Fields

| Field                  | Description                                            |
| ---------------------- | ------------------------------------------------------ |
| `entry.rank`           | Position among all entries (1 = top)                   |
| `entry.score`          | Raw metric, not boosted                                |
| `entry.points`         | Boost-adjusted score                                   |
| `entry.totalRewards`   | Lifetime USD received from this empire's distributions |
| `boosters[].qualified` | Whether this address meets the booster requirement     |

***

### Notes

* Returns `404` if the address does not appear in the leaderboard.
* `boosters[].qualified` reflects the address's current on-chain holdings at the time of the last leaderboard refresh.
* To refresh leaderboard scores, see Refresh Leaderboard By Empire.


# Get Top Empires

Retrieves a paginated list of top empires by their ranking

Only empires with at least one distribution are included.

```
GET /api/top-empires
```

***

### Query Parameters

| Parameter | Type   | Default | Description      |
| --------- | ------ | ------- | ---------------- |
| `page`    | number | `1`     | Page number      |
| `limit`   | number | `20`    | Results per page |

***

### Example

```bash
curl "https://empirebuilder.world/api/top-empires?page=1&limit=20"
```

***

### Response

```json
{
  "empires": [
    {
      "base_token": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b",
      "token_name": "glonkybot",
      "token_symbol": "GLANKER",
      "total_distributed": 1647,
      "total_burned": 251059054,
      "logo_uri": "https://www.empirebuilder.world/EBblackjpg2.jpg"
    }
  ],
  "totalCount": 142,
  "queryTime": 12.4,
  "page": 1,
  "itemsPerPage": 20
}
```

***

### Response Fields

| Field               | Description                                   |
| ------------------- | --------------------------------------------- |
| `base_token`        | **Empire ID** — shapes in **Empire ID** below |
| `token_name`        | Empire / token name                           |
| `token_symbol`      | Token ticker                                  |
| `total_distributed` | Cumulative USD distributed to holders         |
| `total_burned`      | Cumulative tokens burned (raw units)          |
| `logo_uri`          | Token logo URL                                |
| `totalCount`        | Total number of qualifying empires            |
| `queryTime`         | Server-side query duration in milliseconds    |
| `page`              | Current page                                  |
| `itemsPerPage`      | Results per page                              |

***

### Notes

* Empires with `total_distributed = 0` are excluded.
* Ordered by computed `rank` descending, then `total_distributed` descending as a tiebreaker.
* Hidden empires are excluded.

***

### Empire ID (`[empire_id]`)

Each **`base_token`** in the response identifies an empire (**`base_token`** in the database).

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Authenticated


# Add Booster

Adds a new score multiplier booster to an empire.

Requires an API key and a valid signature from a **guardian**.

***

```
POST /api/boosters/[empire_id]
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "booster": {
    "type": "NFT",
    "contractAddress": "0xNFTContractAddress",
    "multiplier": 1.5,
    "requirement": {
      "minAmount": "1"
    },
    "chainId": 8453,
    "tokenId": null
  },
  "signer": "0xOwnerWallet",
  "signature": "0x...",
  "message": "Add booster for empire id <empire_id>",
  "tokenInfo": {
    "name": "My NFT",
    "symbol": "MNFT",
    "logoURI": "https://..."
  }
}
```

***

### Booster Object Fields

| Field                   | Type           | Description                                                          |
| ----------------------- | -------------- | -------------------------------------------------------------------- |
| `type`                  | string         | `NFT`, `ERC20`, or `QUOTIENT`                                        |
| `contractAddress`       | string         | Contract to check holdings against. Use zero address for `QUOTIENT`. |
| `multiplier`            | number         | Score multiplier (e.g. `1.5` = 50% boost). Range: 1.1–5.0            |
| `requirement.minAmount` | string         | Minimum balance / count / score to receive the boost                 |
| `chainId`               | number         | Chain where the contract lives (default `8453`)                      |
| `tokenId`               | string \| null | ERC-1155 token ID (omit or `null` for ERC-721 or ERC-20)             |

***

### Signature

Sign `message` with EIP-191 (`signMessage`). **The API does not parse or validate the wording** — it only checks the signature matches `message`, and that **`signer` is a guardian** for the `[empire_id]` path. **Recommended convention:** include the **empire id** verbatim (same as **`base_token`**: ERC‑20 `0x…`, `fid…`, or custom slug) so wallets show clearly which empire is targeted:

```js
const message = `Add booster for empire id ${empireId}`;
const signature = await signer.signMessage(message);
```

`empireId` must match `[empire_id]` in `POST …/boosters/[empire_id]`.

***

### Response

```json
{
  "boosters": [...],
  "tokenInfoMap": {}
}
```

Returns the full updated booster list for the empire.

***

### Notes

* Multipliers compound multiplicatively with other boosters on the same empire.
* To view all current boosters, see Get Boosters By Empire.
* To remove a booster, see Remove Booster.

***

### Empire ID (`[empire_id]`)

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Remove Booster

Removes a booster from an empire.

Requires an API key and a valid signature from a **guardian**.

```
DELETE /api/boosters/[empire_id]
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "boosterId": "uuid-of-booster-to-remove",
  "signer": "0xOwnerWallet",
  "signature": "0x...",
  "message": "Remove booster for empire id <empire_id>"
}
```

| Field       | Type   | Description                                                          |
| ----------- | ------ | -------------------------------------------------------------------- |
| `boosterId` | string | UUID of the booster to remove (from `GET /api/boosters/[empire_id]`) |
| `signer`    | string | Wallet address that signed the message                               |
| `signature` | string | EIP-191 signature of `message`                                       |
| `message`   | string | Plain-text message that was signed                                   |

***

### Signature

**Recommended convention:** include the **empire id** in `message`:

```js
const message = `Remove booster for empire id ${empireId}`;
const signature = await signer.signMessage(message);
```

***

### Response

```json
{
  "boosters": [...],
  "tokenInfoMap": {}
}
```

Returns the full updated booster list after removal.

***

### Notes

* To find the `boosterId`, call `GET /api/boosters/[empire_id]` and use the `id` field from the booster you want to remove.
* After removal, the next leaderboard refresh will recompute `points` without the removed booster.

***

### Empire ID (`[empire_id]`)

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Add Staking Booster

Adds a staking booster to an empire.

```
POST /api/staking-boosters/[empire_id]
```

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "minStake": "1000000000000000000000",
  "minLockupSeconds": 7776000,
  "multiplier": 1.5,
  "signer": "0xGuardianWallet",
  "signature": "0x...",
  "message": "Add staking booster for empire id <empire_id>"
}
```

| Field              | Type   | Description                                                                                                                                                                                                                                                                  |
| ------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `minStake`         | string | Minimum qualifying stake as an **integer string in wei** (18‑decimal units). The on-chain stake `amount`s summed for qualification use the same base units. *(The empire web app’s form takes whole tokens and multiplies by `10^18` client-side before POST.)*              |
| `minLockupSeconds` | number | Required minimum committed lock-up per qualifying stake (in seconds). Any non-negative integer up to `315_360_000` (10 years), i.e. aligned with StakingLocker’s `MAX_LOCK_DURATION`. `0` means any lock duration qualifies (including flexible `lockDuration == 0` stakes). |
| `multiplier`       | number | Boost amount (1.1 – 5.0, **at most one decimal**).                                                                                                                                                                                                                           |
| `signer`           | string | Guardian wallet address that signed `message`.                                                                                                                                                                                                                               |
| `signature`        | string | EIP-191 signature of `message`.                                                                                                                                                                                                                                              |
| `message`          | string | Plain-text message that was signed.                                                                                                                                                                                                                                          |

The server stores rows with `booster_type = 'STAKING'` in the `boosters` table. The `booster_address` is implicitly set to the empire's own ERC-20.

***

### Signature

The signature must be valid and `signer` must be an Empire guardian.

```js
const message = `Add staking booster for empire id ${empireId}`;
const signature = await signer.signMessage(message);
```

***

### Response

```json
{
  "stakingBoosters": [
    {
      "id": "uuid",
      "type": "STAKING",
      "multiplier": 1.5,
      "requirement": { "minAmount": "1000000000000000000000" },
      "min_lockup_seconds": 7776000
    }
  ],
  "boosters": [...]
}
```

`stakingBoosters` is the filtered subset; `boosters` is the full updated list (same shape as `GET /api/boosters/[empire_id]`).

***

### Errors

| Status | Cause                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------- |
| `400`  | Missing fields, `minLockupSeconds` outside `[0, 315360000]`, or `multiplier` outside 1.1–5.0 / more than one decimal |
| `401`  | Invalid signature                                                                                                    |
| `403`  | Signer is not a guardian, or staking is not activated for this empire                                                |
| `400`  | Empire booster cap reached                                                                                           |

***

### Empire ID (`[empire_id]`)

Same Empire ID conventions as the rest of the boosters API — see [Add Booster](/empire-builder-docs/empire-builder-docs/api/authenticated/add-booster).


# Remove Staking Booster

Removes a Staking booster from an empire.

```
DELETE /api/staking-boosters/[empire_id]
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "boosterId": "uuid-of-staking-booster",
  "signer": "0xGuardianWallet",
  "signature": "0x...",
  "message": "Remove staking booster <boosterId> for empire id <empire_id>"
}
```

| Field       | Type   | Description                                                                           |
| ----------- | ------ | ------------------------------------------------------------------------------------- |
| `boosterId` | string | UUID of the staking booster to remove (from `GET /api/staking-boosters/[empire_id]`). |
| `signer`    | string | Guardian wallet address that signed `message`.                                        |
| `signature` | string | EIP-191 signature of `message`.                                                       |
| `message`   | string | Plain-text message that was signed.                                                   |

The route only deletes rows with `booster_type = 'STAKING'` for this empire — it will not touch ERC-20, NFT, or Reputation boosters.

***

### Response

```json
{
  "stakingBoosters": [...],
  "boosters": [...]
}
```

`stakingBoosters` is the filtered subset after removal; `boosters` is the full updated list (same shape as `GET /api/boosters/[empire_id]`).

***

### Errors

| Status | Cause                                                                 |
| ------ | --------------------------------------------------------------------- |
| `400`  | Missing fields                                                        |
| `401`  | Invalid signature                                                     |
| `403`  | Signer is not a guardian, or staking is not activated for this empire |
| `404`  | No STAKING booster with that id for this empire                       |

***

### List existing staking boosters

```
GET /api/staking-boosters/[empire_id]
```

Returns `{ stakingBoosters: Booster[] }` — the same shape as `GET /api/boosters/[empire_id]` but filtered to `type === "STAKING"`.

***

### Empire ID (`[empire_id]`)

Same Empire ID conventions as the rest of the boosters API — see [Remove Booster.](/empire-builder-docs/empire-builder-docs/api/authenticated/remove-booster)


# Activate Staking

Turns on staking for an empire.

Requires an API key and a valid signature from a **guardian**.

```
POST /api/empires/activate-staking
```

Once activated:

* The empire token can be staked through the immutable [Staking Locker contract](/empire-builder-docs/empire-builder-docs/contracts/staking-locker)
* A new `leaderboard_type = "stakers"` tab is added automatically.
* Staked balances are folded into all `tokenHolders` and `farToken` leaderboard refreshes (existing leaderboards on the empire automatically benefit).
* Guardians can add **STAKING boosters** via [Add Staking Booster.](/empire-builder-docs/empire-builder-docs/api/authenticated/add-staking-booster)

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "tokenAddress": "<empire_id>",
  "signer": "0xGuardianWallet",
  "signature": "0x...",
  "timestamp": 1759353600000
}
```

| Field          | Type   | Description                                                                                                    |
| -------------- | ------ | -------------------------------------------------------------------------------------------------------------- |
| `tokenAddress` | string | **Empire ID** (same `base_token` rules as other empire endpoints).                                             |
| `signer`       | string | Guardian wallet address that signed the message.                                                               |
| `signature`    | string | EIP-191 signature of the canonical message.                                                                    |
| `timestamp`    | number | Millisecond Unix timestamp included in the signed message. The server rejects timestamps older than 5 minutes. |

#### Canonical message format

The server reconstructs the message it expects and verifies. Sign **exactly** this string:

```
Activate staking for empire <empire_id_lowercase> at <timestamp>
```

Example:

```js
const timestamp = Date.now();
const message = `Activate staking for empire ${empireId.toLowerCase()} at ${timestamp}`;
const signature = await signer.signMessage(message);
```

***

### Response

```json
{
  "success": true,
  "stakingToken": "0xDeployedErc20",
  "chainId": 8453,
  "leaderboardId": "uuid-of-auto-created-stakers-leaderboard"
}
```

If staking is already active, the response is `{ "success": true, "alreadyActive": true }`.

***

### Errors

| Status | Cause                                                                                                                       |
| ------ | --------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Missing fields, expired `timestamp`, no ERC-20 attached to a tokenless empire, or staking unsupported on the empire's chain |
| `401`  | Invalid signature                                                                                                           |
| `403`  | Signer is not a guardian for this empire                                                                                    |
| `404`  | Empire not found                                                                                                            |

***

### GET — read activation state

```
GET /api/empires/activate-staking?tokenAddress=<empire_id>
```

Response:

```json
{
  "staking_activated": true,
  "stakingToken": "0xDeployedErc20",
  "chainId": 8453
}
```

This is what the empire page uses to decide whether to show "Stake" vs "Activate Staking".

***

### Empire ID (`[empire_id]`)

Same Empire ID conventions as the rest of the API — see [Add Booster](/empire-builder-docs/empire-builder-docs/api/authenticated/add-booster).


# Create Leaderboard

Creates a new leaderboard for an empire.

Requires an API key and a valid signature from a **guardian**.&#x20;

Slot numbering, tabs, and credit limits are summarized in **Leaderboards**.

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Leaderboard Types

#### Token Holders

```
POST /api/leaderboards/tokenHoldersLeaderboards
```

Ranked by holdings of any ERC-20 token.

```json
{
  "tokenAddress": "<empire_id>",
  "erc20Address": "0xTokenToRankBy",
  "erc20ChainId": 8453,
  "erc20Name": "Degen",
  "erc20Symbol": "DEGEN",
  "erc20ImageUrl": "https://...",
  "name": "DEGEN Holders",
  "description": "Ranked by DEGEN balance",
  "applyBoosters": true,
  "signature": "0x...",
  "message": "Create leaderboard for <empire_id>",
  "signerAddress": "0xGuardianWallet"
}
```

***

#### Stakers

```
POST /api/leaderboards/stakersLeaderboards
```

Ranked by **on-chain stake** in the immutable StakingLocker contract for any ERC-20 token. Available on Base (`8453`) and Arbitrum (`42161`). The empire must have staking activated for this leaderboard's auto-creation flow on the strip; the API itself can be called for any ERC-20 the contract has stakes for.

```json
{
  "tokenAddress": "empireid",
  "erc20Address": "0xTokenToRankBy",
  "erc20ChainId": 8453,
  "erc20Name": "MyToken",
  "erc20Symbol": "MYT",
  "erc20ImageUrl": "https://...",
  "applyBoosters": true,
  "applyReputationBoosters": true,
  "applyStakingBoosters": true,
  "signature": "0x...",
  "message": "Create stakers leaderboard for 0xTokenToRankBy on chain 8453",
  "signerAddress": "0xOwnerWallet"
}
```

Notes:

* Reads stakers + balances directly from the StakingLocker (no Ankr / Moralis dependency).
* Boosters apply additively on top of staked balance: token, reputation, and STAKING boosters (capped at +5x for the staking portion).
* Listed under `leaderboard_type = "stakers"` and refreshed via `PATCH /api/leaderboards/refresh/stakersLeaderboards`.

***

#### NFT Holders

```
POST /api/leaderboards/nftLeaderboards
```

Ranked by NFT holdings (ERC-721 or ERC-1155).

```json
{
  "tokenAddress": "<empire_id>",
  "nftAddress": "0xNFTContractAddress",
  "chainId": 8453,
  "tokenId": null,
  "nftImage": "https://...",
  "name": "NFT Collectors",
  "description": "Ranked by NFT holdings",
  "applyBoosters": true,
  "apply_reputation_boosters": false,
  "signature": "0x...",
  "message": "Create NFT leaderboard for <empire_id>",
  "signerAddress": "0xGuardianWallet"
}
```

`tokenId` — ERC-1155 token ID. Omit or `null` for ERC-721.

***

#### API-Sourced

```
POST /api/leaderboards/apiLeaderboards
```

Populated from an external API endpoint you control.

```json
{
  "tokenAddress": "<empire_id>",
  "apiEndpoint": "https://yourapi.com/scores",
  "name": "Custom Scores",
  "description": "Ranked by your custom metric",
  "applyBoosters": false,
  "apply_reputation_boosters": false,
  "icon": "https://...",
  "signature": "0x...",
  "message": "Create API leaderboard for <empire_id>",
  "signerAddress": "0xGuardianWallet"
}
```

Your `apiEndpoint` must return JSON in this format:

```json
[
  { "address": "0x...", "score": 1500 },
  { "address": "0x...", "score": 900 }
]
```

***

#### CSV Upload

```
POST /api/leaderboards/csvLeaderboards
Content-Type: multipart/form-data
```

| Form Field      | Description                                                    |
| --------------- | -------------------------------------------------------------- |
| `tokenAddress`  | **Empire ID** (`base_token`; see **Empire ID** at end of page) |
| `name`          | Display name (max 20 chars)                                    |
| `description`   | Description (max 180 chars)                                    |
| `file`          | CSV file with columns: `address`, `score`                      |
| `applyBoosters` | `true` or `false`                                              |
| `signature`     | EIP-191 signature                                              |
| `message`       | Signed message                                                 |
| `signerAddress` | Signing wallet                                                 |

***

#### Farcaster Leaderboards

All four Farcaster types use the same base body shape:

```json
{
  "tokenAddress": "<empire_id>",
  "name": "Farcaster Fans",
  "description": "Ranked by Farcaster activity",
  "applyBoosters": true,
  "signature": "0x...",
  "message": "Create Farcaster leaderboard for <empire_id>",
  "signerAddress": "0xGuardianWallet"
}
```

| Type               | Endpoint                                                  |
| ------------------ | --------------------------------------------------------- |
| Cast activity      | `POST /api/leaderboards/farcasterCastLeaderboards`        |
| Channel activity   | `POST /api/leaderboards/farcasterChannelLeaderboards`     |
| Interactions       | `POST /api/leaderboards/farcasterInteractionLeaderboards` |
| Far token holdings | `POST /api/leaderboards/farTokenLeaderboards`             |

***

### Common Required Fields

| Field           | Description                                                    |
| --------------- | -------------------------------------------------------------- |
| `tokenAddress`  | **Empire ID** (`base_token`; see **Empire ID** at end of page) |
| `signature`     | EIP-191 signature of `message` by a **guardian**               |
| `message`       | Plain-text message that was signed                             |
| `signerAddress` | Wallet address that signed (must be a **guardian**)            |
| `name`          | Display name (max 20 chars)                                    |
| `description`   | Description (max 180 chars)                                    |

***

### Response

```json
{
  "success": true,
  "leaderboard": {
    "id": "uuid",
    "leaderboard_number": 2,
    "leaderboard_type": "tokenHolders",
    "name": "DEGEN Holders"
  }
}
```

***

### Empire ID (`[empire_id]`)

In every leaderboard **`POST`** body above, **`tokenAddress`** is your **empire id** (**`base_token`**) — the same **`[empire_id]`** you use under `/api/empires/[empire_id]` and related APIs. Not always an ERC‑20 contract address.

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages, it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Delete Leaderboard

Deletes a leaderboard from an empire.

Requires an API key **and** a valid **EIP-191 signature** from an empire **guardian**.

***

```
DELETE /api/leaderboards/delete?leaderboardId=[uuid]
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
x-wallet-address: 0xGuardianWallet
Content-Type: application/json
```

***

### Query Parameters

| Parameter       | Description                       |
| --------------- | --------------------------------- |
| `leaderboardId` | UUID of the leaderboard to delete |

***

### Request Body

```json
{
  "signature": "0x...",
  "message": "Human-readable string the guardian signed",
  "signerAddress": "0xGuardianWallet"
}
```

| Field           | Type   | Description                                                                                   |
| --------------- | ------ | --------------------------------------------------------------------------------------------- |
| `signature`     | string | Guardian’s signature over `message`                                                           |
| `message`       | string | Payload that was signed (your app chooses the text; it must match what you verify)            |
| `signerAddress` | string | Must equal `x-wallet-address` and be **owner** or **co-emperor** for the leaderboard’s empire |

***

### Response

```json
{
  "success": true
}
```

***

### Notes

* Deleting a leaderboard removes all associated entries and cannot be undone.
* To find the `leaderboardId`, call `GET /api/leaderboards?tokenAddress=[empire_id]`.

***

### Empire ID (`[empire_id]`)

This endpoint does not take **`tokenAddress`** / empire id in the body. To list leaderboards (and read each **`id`** / UUID), use **`GET /api/leaderboards?tokenAddress=[empire_id]`** — there **`tokenAddress`** is the empire’s **`base_token`** / **`[empire_id]`**.

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Deploy Empire for Existing Token

Deploy an Empire for an existing token.

Use this when you want to attach an Empire for a token that already exists on-chain.

***

```
POST /api/deploy-empire
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request body

**Required:** `baseToken`, `name`, `owner`, `signature`, `message`, and `chainId` for the chain where the token lives.

```json
{
  "baseToken": "0xExistingTokenAddress",
  "name": "My Empire",
  "owner": "0xYourWallet",
  "chainId": 8453,
  "tokenInfo": {
    "symbol": "MTK",
    "name": "My Token",
    "logoURI": "https://..."
  },
  "signature": "0x...",
  "message": "Deploy empire for My Token",
  "empireMetadata": {
    "bio": "A community token",
    "website_url": "https://mytoken.xyz",
    "twitter_url": "https://x.com/mytoken",
    "warpcast_url": "https://warpcast.com/mytoken"
  }
}
```

| Field            | Type   | Description                                               |
| ---------------- | ------ | --------------------------------------------------------- |
| `baseToken`      | string | Existing token contract address                           |
| `name`           | string | Empire display name                                       |
| `owner`          | string | Owner wallet address (must match the signature)           |
| `chainId`        | number | Token chain: **`8453`** (Base) or **`42161`** (Arbitrum)  |
| `tokenInfo`      | object | `{ symbol, name, logoURI }`                               |
| `signature`      | string | EIP-191 signature of `message` by `owner`                 |
| `message`        | string | Plain-text message that was signed                        |
| `empireMetadata` | object | Optional — bio, website, Twitter, Warpcast, Telegram URLs |

***

### Response

```json
{
  "success": true,
  "empireAddress": "0xVaultAddress",
  "treasuryAddress": "0xVaultAddress",
  "transactionHash": "0x...",
  "empires": [
    { "chainId": 8453, "empireAddress": "0x...", "transactionHash": "0x..." },
    { "chainId": 42161, "empireAddress": "0x...", "transactionHash": "0x..." }
  ],
  "empire": {
    "name": "My Empire",
    "baseToken": "0x...",
    "empireAddress": "0x...",
    "owner": "0x...",
    "tokenType": "topHolder",
    "chainId": 8453,
    "deployedAt": "2024-06-01T12:00:00Z",
    "status": "active",
    "native": "no"
  }
}
```

***

### Notes

* **`signature`** / **`owner`**: **`message`** must be signed by **`owner`** (EIP-191), and **`owner`** must pass the verification checks for controlling **`baseToken`** — for example, being the onchain **`owner()`**, or **`admin()`**, or type-specific rules (Clanker, Zora, top-holder eligibility).
* **Rate limit:** 2 deployments per wallet per 24 hours (same pattern as `deploy-empire`).
* **409** if an empire already exists for that **`baseToken`**.


# Deploy Tokenless Empire

Deploy an Empire for a Farcaster profile or custom hub

```
POST /api/deploy-empire-tokenless
```

This is **not** `POST /api/deploy-empire` (on-chain token addresses).

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request body

#### Farcaster tokenless

```json
{
  "mode": "farcaster",
  "owner": "0xYourWallet",
  "name": "My Farcaster Empire",
  "fid": 373666,
  "farcasterUsername": "optional_hint",
  "logoUri": "https://...",
  "bio": "Short description (max 2000 chars)",
  "signature": "0x...",
  "message": "I am deploying a tokenless Farcaster Empire with Farcaster ID 373666 and name My Farcaster Empire"
}
```

| Field               | Type   | Required | Description                                           |
| ------------------- | ------ | -------- | ----------------------------------------------------- |
| `mode`              | string | ✅        | `"farcaster"`                                         |
| `owner`             | string | ✅        | Guardian wallet (0x + 40 hex)                         |
| `name`              | string | ✅        | Empire display name (≤ 100 chars)                     |
| `fid`               | number | ✅        | Farcaster ID; must match Neynar’s FID for **`owner`** |
| `farcasterUsername` | string | ✅        | Used for leaderboard naming                           |
| `logoUri`           | string | —        | Logo URL                                              |
| `bio`               | string | —        | Stored as `empires.bio` (≤ 2000 chars)                |
| `signature`         | string | ✅        | EIP-191 signature                                     |
| `message`           | string | ✅        | Must equal the expected template above                |

#### Custom tokenless

```json
{
  "mode": "custom",
  "owner": "0xYourWallet",
  "name": "Glönk",
  "logoUri": "https://...",
  "bio": "Optional bio",
  "signature": "0x...",
  "message": "I am deploying a custom tokenless Empire named Glönk"
}
```

| Field       | Type   | Required | Description                            |
| ----------- | ------ | -------- | -------------------------------------- |
| `mode`      | string | ✅        | `"custom"`                             |
| `owner`     | string | ✅        | Guardian wallet                        |
| `name`      | string | ✅        | Empire display name (≤ 100 chars)      |
| `logoUri`   | string | —        | Logo URL                               |
| `bio`       | string | —        | Stored as `empires.bio` (≤ 2000 chars) |
| `signature` | string | ✅        | EIP-191 signature                      |
| `message`   | string | ✅        | Must equal the expected template above |

***

### Response

```json
{
  "success": true,
  "treasuryAddress": "0xVaultAddress",
  "empireAddress": "0xVaultAddress",
  "transactionHash": "0x...",
  "baseToken": "fid373666",
  "empires": [
    { "chainId": 8453, "empireAddress": "0x...", "transactionHash": "0x...", "blockNumber": 123456, "gasUsed": "200000" },
    { "chainId": 42161, "empireAddress": "0x...", "transactionHash": "0x...", "blockNumber": 789012, "gasUsed": "180000" }
  ],
  "empire": {
    "name": "My Farcaster Empire",
    "baseToken": "fid373666",
    "empireAddress": "0x...",
    "owner": "0x...",
    "tokenType": "farcaster_tokenless",
    "chainId": 8453
  }
}
```

After a successful deploy, use the JSON **`baseToken`** anywhere an empire id is needed (`GET /api/empires/...`, leaderboards, `/empire/{base_token}` URLs, etc.).

***

### Notes

* **Rate limit:** 2 deployments per wallet per 24 hours (same pattern as `deploy-empire`).
* **409** if an empire already exists for that **`base_token`**.
* **Multisig threshold:** factory call uses threshold **1**; for Farcaster mode, if Neynar reports a **primary ETH address** for the FID that differs from **`owner`**, that address is added as an additional signer on the vault.

***


# Deploy New Token with Empire

Deploy a new Clanker token with an attached Empire in a single flow.

This is the recommended path for new token launches.

The process has three steps: generate a Clanker SDK config server-side, deploy the token on-chain with the Clanker SDK, then register the empire.

***

### Step 1 — Generate Token Config

```
POST /api/get-token-config
```

Builds and returns a fully-formed Clanker v4 SDK `tokenConfig` object. This includes pool configuration, fee structure, vault settings, dev buy, airdrop Merkle tree, and sniper fees.

**Headers**

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

**Body**

```json
{
  "name": "My Token",
  "symbol": "MTK",
  "imageUrl": "https://...",
  "creatorAddress": "0xYourWallet",

  "poolType": "standard",
  "feeType": "dynamic",
  "initialMarketCap": 10,

  "dynamicBaseFee": 1,
  "dynamicMaxLpFee": 2.2,

  "vaultPercentage": 10,
  "vaultDays": 180,

  "enableDevBuy": false,
  "devBuyAmount": 0,

  "enableAirdrop": false,
  "airdropEntries": [],
  "airdropLockupDays": 30,
  "airdropVestingDays": 0,

  "enableSniperFees": false,
  "sniperFeeDuration": 15,

  "tokenDescription": "A great token",
  "socialTwitter": "https://x.com/mytoken",
  "socialFarcaster": "https://warpcast.com/mytoken",
  "socialWebsite": "https://mytoken.xyz",
  "socialTelegram": ""
}
```

**Body Fields**

| Field                | Type    | Default    | Description                                          |
| -------------------- | ------- | ---------- | ---------------------------------------------------- |
| `name`               | string  | —          | Token name                                           |
| `symbol`             | string  | —          | Token ticker                                         |
| `imageUrl`           | string  | —          | Token logo URL                                       |
| `creatorAddress`     | string  | —          | Deploying wallet (fee routing + FID lookup)          |
| `poolType`           | string  | `standard` | `standard` (single range) or `project` (multi-range) |
| `feeType`            | string  | `dynamic`  | `dynamic` or `static`                                |
| `initialMarketCap`   | number  | `10`       | Starting market cap in ETH — sets the initial tick   |
| `dynamicBaseFee`     | number  | `1`        | Base LP fee % (dynamic mode)                         |
| `dynamicMaxLpFee`    | number  | `2.2`      | Max LP fee % (dynamic mode)                          |
| `staticClankerFee`   | number  | `1`        | Clanker protocol fee % (static mode)                 |
| `staticPairedFee`    | number  | `1`        | Paired-token fee % (static mode)                     |
| `vaultPercentage`    | number  | —          | % of supply to lock in SmartVault                    |
| `vaultDays`          | number  | —          | Vault lockup duration in days                        |
| `enableDevBuy`       | boolean | `false`    | Include a dev buy at launch                          |
| `devBuyAmount`       | number  | `0`        | ETH amount for dev buy                               |
| `enableAirdrop`      | boolean | `false`    | Include launch airdrop                               |
| `airdropEntries`     | array   | `[]`       | `[{ address, amount }]` recipient list               |
| `airdropLockupDays`  | number  | —          | Days before claims unlock (min 1 if airdrop enabled) |
| `airdropVestingDays` | number  | `0`        | Days to vest after unlock                            |
| `enableSniperFees`   | boolean | `false`    | Apply elevated fees to early buyers                  |
| `sniperFeeDuration`  | number  | `15`       | Seconds sniper fees stay active                      |
| `tokenDescription`   | string  | —          | Clanker v4 metadata description                      |
| `socialTwitter`      | string  | —          | Twitter/X URL                                        |
| `socialFarcaster`    | string  | —          | Warpcast URL                                         |
| `socialWebsite`      | string  | —          | Website URL                                          |
| `socialTelegram`     | string  | —          | Telegram URL                                         |

**Response**

```json
{
  "success": true,
  "tokenConfig": {
    "name": "My Token",
    "symbol": "MTK",
    "tokenAdmin": "0xYourWallet",
    "image": "https://...",
    "pool": { "tickIfToken0IsClanker": -192000, "positions": [...] },
    "fees": { "type": "dynamic", "baseFee": 100, "maxFee": 220 },
    "rewards": { "recipients": [...] },
    "vault": { "percentage": 10, "lockupDuration": 15552000, "vestingDuration": 0 }
  },
  "airdropTree": null
}
```

***

### Step 2 — Deploy On-Chain with Clanker SDK

Use the `tokenConfig` from Step 1 to call the Clanker SDK's deploy function in the browser.

```ts
import { deployToken } from "clanker-sdk/v4";
import { useWalletClient } from "wagmi";

const walletClient = useWalletClient();

// tokenConfig is from /api/get-token-config
const result = await deployToken(walletClient, tokenConfig, {
  airdropTree: airdropTree ?? undefined,
});

const deployedTokenAddress = result.tokenAddress; // 0x...
const deployTxHash = result.hash;                 // 0x...
```

***

### Step 3 — Register the Empire

```
POST /api/deploy-empire
```

After the Clanker token is deployed, call this endpoint with the deploy **`txHash`** so the server can verify the on-chain transaction and register the empire.

**Headers**

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

**Body**

```json
{
  "baseToken": "0xDeployedTokenAddress",
  "name": "My Empire",
  "owner": "0xYourWallet",
  "clankerVersion": "clanker_v4",
  "txHash": "0xDeployTransactionHash",
  "chainId": 8453,
  "tokenInfo": {
    "symbol": "MTK",
    "name": "My Token",
    "logoURI": "https://..."
  },
  "empireMetadata": {
    "bio": "A community token",
    "website_url": "https://mytoken.xyz",
    "twitter_url": "https://x.com/mytoken",
    "warpcast_url": "https://warpcast.com/mytoken"
  }
}
```

| Field            | Type   | Description                                                        |
| ---------------- | ------ | ------------------------------------------------------------------ |
| `baseToken`      | string | Deployed token address (from Step 2)                               |
| `name`           | string | Empire display name                                                |
| `owner`          | string | Owner wallet address                                               |
| `clankerVersion` | string | `clanker_v4`                                                       |
| `txHash`         | string | **Required for this flow** — transaction hash from Step 2          |
| `chainId`        | number | Chain where the token was deployed (`8453` Base, `42161` Arbitrum) |
| `tokenInfo`      | object | `{ symbol, name, logoURI }`                                        |
| `empireMetadata` | object | Optional — bio and social URLs                                     |

**Response**

```json
{
  "success": true,
  "empireAddress": "0xVaultAddress",
  "treasuryAddress": "0xVaultAddress",
  "transactionHash": "0x...",
  "empires": [
    { "chainId": 8453, "empireAddress": "0x...", "transactionHash": "0x...", "blockNumber": 123456 },
    { "chainId": 42161, "empireAddress": "0x...", "transactionHash": "0x...", "blockNumber": 789012 }
  ],
  "empire": {
    "name": "My Empire",
    "baseToken": "0x...",
    "empireAddress": "0x...",
    "owner": "0x...",
    "tokenType": "clanker",
    "chainId": 8453,
    "deployedAt": "2024-06-01T12:00:00Z",
    "status": "active",
    "native": "no"
  }
}
```

***

### Full Flow Summary

```
1. POST /api/get-token-config              → tokenConfig + airdropTree
2. deployToken(walletClient, tokenConfig)  → deployedTokenAddress + txHash  (Clanker SDK, client-side)
3. POST /api/deploy-empire                 → empireAddress + empire record
```


# Refresh Leaderboard By Empire

Updates and overwrites the scores for a specific Empire leaderboard and returns the updated rankings.

Each leaderboard type has a dedicated refresh endpoint. Refresh pulls fresh on-chain or external scores and rewrites all entries. There is a **30-second cooldown** between refreshes per leaderboard.

***

### Authentication

```
x-api-key: YOUR_API_KEY
```

***

### Refresh Endpoints by Type

| Leaderboard Type      | Endpoint                                                           |
| --------------------- | ------------------------------------------------------------------ |
| Token holders         | `PATCH /api/leaderboards/refresh/tokenHoldersLeaderboards`         |
| Stakers               | `PATCH /api/leaderboards/refresh/stakersLeaderboards`              |
| NFT holdings          | `PATCH /api/leaderboards/refresh/nftLeaderboards`                  |
| API-sourced           | `PATCH /api/leaderboards/refresh/apiLeaderboards`                  |
| CSV                   | `PATCH /api/leaderboards/refresh/csvLeaderboards`                  |
| Farcaster cast        | `PATCH /api/leaderboards/refresh/farcasterCastLeaderboards`        |
| Farcaster channel     | `PATCH /api/leaderboards/refresh/farcasterChannelLeaderboards`     |
| Farcaster interaction | `PATCH /api/leaderboards/refresh/farcasterInteractionLeaderboards` |
| Far token             | `PATCH /api/leaderboards/refresh/farTokenLeaderboards`             |

***

### Request Body

```json
{
  "leaderboardId": "uuid",
  "tokenAddress": "empireid"
}
```

Both fields are required. **`tokenAddress`** is your **Empire ID** (**`base_token`** / **`[empire_id]`** — see **Empire ID** at end of page).

***

### Example

```bash
curl -X PATCH "https://empirebuilder.world/api/leaderboards/refresh/tokenHoldersLeaderboards" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"leaderboardId": "uuid-here", "tokenAddress": "0x..."}'
```

***

### Response

```json
{
  "success": true,
  "leaderboardId": "uuid-here",
  "entriesUpdated": 142,
  "refreshedAt": "2024-06-01T12:00:00Z"
}
```

***

### Notes

* Returns `429` if called within 30 seconds of the last refresh for the same leaderboard.
* Refresh rewrites all entries — scores, ranks, and points are all recomputed.
* Booster multipliers are re-applied during refresh if the leaderboard has `applyBoosters: true`.

***

### Empire ID (`[empire_id]`)

Request body **`tokenAddress`** identifies the empire (**`base_token`** / **`[empire_id]`**).

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Store Clanker Airdrop

Registers a Clanker launch airdrop recipient list and claim records after the airdrop contract has been deployed on-chain.

Requires an API key.

Stores the Merkle root, lockup/vesting schedule, and per-recipient claim records (all initially `claimed: false`).

```
POST /api/store-airdrop
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "tokenAddress": "0x...",
  "tokenName": "My Token",
  "tokenSymbol": "MTK",
  "creatorAddress": "0xOwnerWallet",
  "airdropEntries": [
    { "address": "0x...", "amount": 1000 },
    { "address": "0x...", "amount": 500 }
  ],
  "deploymentTxHash": "0x...",
  "merkleRoot": "0x...",
  "airdropContractAddress": "0x...",
  "lockupDays": 30,
  "vestingDays": 0,
  "totalAmount": 1500
}
```

| Field                    | Type   | Required | Description                                                                |
| ------------------------ | ------ | -------- | -------------------------------------------------------------------------- |
| `tokenAddress`           | string | yes      | Token contract address                                                     |
| `tokenName`              | string | yes      | Token name                                                                 |
| `tokenSymbol`            | string | yes      | Token ticker                                                               |
| `creatorAddress`         | string | yes      | Creator wallet address                                                     |
| `airdropEntries`         | array  | yes      | `[{ address, amount }]` — amounts >0.1 are rounded to 1; ≤0.1 are set to 0 |
| `deploymentTxHash`       | string | yes      | Tx hash of the airdrop contract deployment                                 |
| `merkleRoot`             | string | no       | Merkle root from the Clanker SDK `createAirdrop()` call                    |
| `airdropContractAddress` | string | no       | Deployed airdrop contract address                                          |
| `lockupDays`             | number | no       | Days before claims unlock (minimum 1 if provided)                          |
| `vestingDays`            | number | no       | Days to vest after unlock                                                  |
| `totalAmount`            | number | no       | Total token amount (calculated from entries if omitted)                    |

***

### Response

```json
{
  "success": true,
  "airdropId": "uuid",
  "message": "Airdrop data stored successfully for 500 recipients",
  "data": {
    "tokenAddress": "0x...",
    "tokenName": "My Token",
    "tokenSymbol": "MTK",
    "totalRecipients": 500,
    "totalAmount": 1500,
    "lockupDays": 30,
    "vestingDays": 0
  }
}
```

***

### Notes

* This endpoint is used for Clanker launch airdrops (Merkle claim model). For push airdrops via the Airdrop contract, see the Airdrop contract docs.
* All claim records are stored with `claimed: false`. On-chain claims update this status separately.
* Call this after `deployToken()` succeeds and you have the `deploymentTxHash` from the Clanker SDK.


# Store Burn

Records a completed on-chain token burn to an Empire

Requires an API key.

```
POST /api/store-burn
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "transactionHash": "0x...",
  "empireAddress": "0xVaultAddress",
  "chainId": 8453
}
```

| Field             | Type   | Default | Description                           |
| ----------------- | ------ | ------- | ------------------------------------- |
| `transactionHash` | string | —       | On-chain tx hash of the burn transfer |
| `empireAddress`   | string | —       | Empire SmartVault address             |
| `chainId`         | number | `8453`  | Chain where the burn occurred         |

***

### Response

```json
{
  "success": true,
  "amount": 5000.0,
  "baseToken": "0x...",
  "empireAddress": "0x...",
  "total_burned": 25000
}
```

***

### Notes

* A burn is any ERC-20 Transfer of the Empire's token with `to` equal to `0x0000000000000000000000000000000000000000` or `0x000000000000000000000000000000000000dEaD`.
* No vault interaction is required — burns can be sent from any wallet.
* `total_burned` in the response reflects the updated cumulative burn total for the empire (raw token units, not USD).


# Store Distribution

Records a completed on-chain distribution and updates per-recipient USD totals.

Requires an API key.

```
POST /api/store-distribution
```

***

### Authentication

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

***

### Request Body

```json
{
  "transactionHashes": [
    { "hash": "0x...", "chainId": 8453 },
    { "hash": "0x...", "chainId": 42161 }
  ],
  "empireAddress": "0xEmpireAddress",
  "baseToken": "EmpireId",
  "distributionMode": "even",
  "leaderboardType": "main",
  "leaderboardNumber": 0,
  "empireDisplayName": "My Empire"
}
```

| Field               | Type   | Default | Description                                                                |
| ------------------- | ------ | ------- | -------------------------------------------------------------------------- |
| `transactionHashes` | array  | —       | `[{ hash, chainId }]` — one or more on-chain distribution txs              |
| `empireAddress`     | string | —       | Empire SmartVault `0x` address (distinct from **`baseToken` / empire id**) |
| `baseToken`         | string | —       | **Empire ID** (`base_token`; see **Empire ID** at end of page)             |
| `distributionMode`  | string | `even`  | `even`, `weighted`, or `raffle`                                            |
| `leaderboardType`   | string | `main`  | `main` or `custom`                                                         |
| `leaderboardNumber` | number | `0`     | Leaderboard slot number (`0` for main)                                     |
| `empireDisplayName` | string | —       | Optional display name for notifications                                    |

***

### Response

```json
{
  "success": true,
  "data": {
    "totalUsdDistributed": 1200.50,
    "recipientCount": 42,
    "chainsProcessed": 2,
    "distributionMode": "even",
    "transactionHashesRecorded": ["0x..."],
    "skippedAlreadyRecorded": 0,
    "skippedTooOld": 0,
    "recipients": [
      { "address": "0x...", "amountUsd": 28.58, "farcasterUsername": "alice" }
    ]
  }
}
```

***

### Notes

* Duplicate hashes are detected before processing and reported in `skippedAlreadyRecorded`.
* Transactions submitted more than 10 minutes ago are rejected and reported in `skippedTooOld`.

***

### Empire ID (`[empire_id]`)

JSON **`baseToken`** is the **empire id** (**`base_token`**) identifying the empire row — token contract `0x…`, **`fid`** id, or custom tokenless slug (not the SmartVault **`empireAddress`**).

The empire identifier is returned as **`base_token`** in empire payloads (e.g. `GET /api/empires/...`).

On empire pages it is the **path segment** immediately after **`empirebuilder.world/empire/`** in the URL (e.g. `https://empirebuilder.world/empire/{empire_id}`).

| Kind                          | Format                                                                                            | Example                                      |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Token empire                  | Deployed empire ERC‑20 base token (42‑char `0x…` hex)                                             | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster profile (tokenless) | `fid` + numeric Farcaster ID                                                                      | `fid373666`                                  |
| Custom tokenless              | Alphanumeric slug from the empire display name plus the last 6 hex characters of the owner wallet | `glonkybota1b2c3`                            |


# Prepare Distribute

Prepare a treasury distribution

Builds a treasury payout plan: loads a leaderboard, reads vault token balances on **Base** and **Arbitrum**, and returns **`executeBatch`** calldata for the Empire Treasury contract.

You broadcast those transactions yourself by [writing to the treasury contract.](/empire-builder-docs/empire-builder-docs/contracts/empire-treasury-write/distribute-tokens) This endpoint does **not** use a bundler or Empire Builder paymaster like the in-app distribution flow.

After your on-chain txs confirm, call [Store Distribution](/empire-builder-docs/empire-builder-docs/api/authenticated/store-distribution) to update per-recipient USD totals and empire counters.

```
POST /api/distribute-prepare
```

***

### Authentication

Requires an **API key** (same as other partner APIs) **and** proof that the signer is the **treasury owner**:

```
x-api-key: YOUR_API_KEY
Content-Type: application/json
```

The JSON body must include **`signature`**, **`message`**, and **`signer`**. The server verifies EIP-191 `signature` over `message` from `signer`, then checks `signer` matches `owner()` on the vault at **`treasuryAddress`** / **`empireAddress`**.

***

### How it works

1. **Auth** — `validateRequest` (API key / allowed origins) passes, then ownership is verified on-chain.
2. **Recipients** — Loads up to **`recipientCount`** addresses from the leaderboard (**`leaderboardId`**: `"main"` or a custom board UUID from `GET /api/leaderboards?tokenAddress=…`).
3. **Balances** — For each token in **`selectedTokenAddresses`**, reads the vault balance on Base and Arbitrum. Only chains with enough balance and **`distributePercentage`** produce transfers.
4. **Amounts** — Splits follow **`distributionMode`**: `even`, `weighted` (by leaderboard `points`), or `raffle` (optional **`raffleWinnerCount`**).
5. **Batches** — Builds ERC-20 `transfer` calls, splits into batches if needed (gas limits), then ABI-encodes **`executeBatch(calls)`** per batch per chain.
6. **Response** — Returns **`transactions[]`**: one entry per on-chain tx you must send. Each includes **`chainId`**, vault **`contractAddress`**, **`calls`**, and full encoded **`data`**.

There is **no** session, **no** Redis, and **no** transaction credit charge on this route. (Credits apply to the **website** sponsored UserOp path only.)

***

### Request body

```json
{
  "treasuryAddress": "0xSmartVault",
  "baseToken": "0xEmpireIdOrFidOrSlug",
  "selectedTokenAddresses": ["0xTokenA", "0xTokenB"],
  "distributionMode": "raffle",
  "leaderboardId": "main",
  "distributePercentage": 100,
  "recipientCount": 100,
  "raffleWinnerCount": 10,
  "signature": "0x...",
  "message": "Distribute treasury tokens",
  "signer": "0xOwnerWallet"
}
```

| Field                                | Type      | Default | Description                                                                                                                 |
| ------------------------------------ | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `treasuryAddress` or `empireAddress` | string    | —       | Treasury **`0x`** address                                                                                                   |
| `baseToken`                          | string    | —       | **Empire ID** (`base_token`): token `0x…`, `fid…`, or custom slug — see [**Empire ID**](#empire-id-basetoken-in-json) below |
| `selectedTokenAddresses`             | string\[] | —       | ERC-20s to distribute from the vault (must have balance on at least one chain)                                              |
| `distributionMode`                   | string    | `even`  | `even`, `weighted`, or `raffle`                                                                                             |
| `leaderboardId`                      | string    | `main`  | `"main"` (default resolved leaderboard) or custom leaderboard UUID                                                          |
| `distributePercentage`               | number    | `100`   | Percentage of vault balance to allocate per token (0–100)                                                                   |
| `recipientCount`                     | number    | `100`   | Max recipients pulled from the leaderboard (capped server-side)                                                             |
| `raffleWinnerCount`                  | number    | —       | Winner count when `distributionMode` is `raffle`                                                                            |
| `signature`                          | string    | —       | EIP-191 signature over `message`                                                                                            |
| `message`                            | string    | —       | Plain text that was signed                                                                                                  |
| `signer`                             | string    | —       | Address that signed; must be vault `owner()`                                                                                |

***

### Response

```json
{
  "vaultAddress": "0xSmartVault",
  "baseToken": "0x...",
  "distributionMode": "weighted",
  "leaderboardId": "main",
  "preview": {
    "recipientCount": 42,
    "tokenAddresses": ["0xTokenA"],
    "chainIds": [8453, 42161]
  },
  "recipients": [{ "address": "0x...", "weight": "90000" }],
  "balanceDetails": [],
  "summary": {
    "transactionCount": 2,
    "byChain": [
      { "chainId": 8453, "batchCount": 1 },
      { "chainId": 42161, "batchCount": 1 }
    ]
  },
  "transactions": [
    {
      "chainId": 8453,
      "batchIndex": 0,
      "contractAddress": "0xSmartVault",
      "functionName": "executeBatch",
      "value": "0",
      "calls": [
        { "target": "0xToken", "value": "0", "data": "0x..." }
      ],
      "data": "0x..."
    }
  ],
  "storeDistribution": {
    "description": "After your executeBatch transactions are confirmed, record them with POST /api/store-distribution (API key required).",
    "exampleRequestFields": {
      "transactionHashes": "array of { hash, chainId } — one entry per mined executeBatch tx",
      "empireAddress": "0xSmartVault",
      "baseToken": "0x...",
      "distributionMode": "weighted",
      "leaderboardType": "main",
      "leaderboardNumber": 0
    }
  }
}
```

| Field                                    | Description                                                                                                                   |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `summary.transactionCount`               | Number of separate **`executeBatch`** transactions to broadcast                                                               |
| `summary.byChain`                        | How many batches on each **`chainId`**                                                                                        |
| `transactions[]`                         | For each tx: mine on **`chainId`** to **`contractAddress`** with **`executeBatch`** and **`calls`**, or submit raw **`data`** |
| `storeDistribution.exampleRequestFields` | Align **`leaderboardType` / `leaderboardNumber`** with prepare (`main`/`0` vs `custom`/UUID) when calling Store Distribution  |

***

### Errors

| Status | Typical cause                                                                            |
| ------ | ---------------------------------------------------------------------------------------- |
| `400`  | Missing fields, no leaderboard entries, no vault balance, or no transfer calls generated |
| `403`  | Invalid signature or signer is not the vault owner                                       |
| `401`  | API key / `validateRequest` failure                                                      |

***

### Related

* Store Distribution — required after txs land
* Get Leaderboard By Empire — preview ranks; list boards via `GET /api/leaderboards?tokenAddress=<empire_id>`
* Distribute Tokens (Bulk Send) — contract-level `executeBatch` reference

***

### Empire ID (`baseToken` in JSON)

JSON **`baseToken`** is the **empire id** (**`base_token`**) identifying the empire row — not the SmartVault address.

| Kind                  | Example                                      |
| --------------------- | -------------------------------------------- |
| Token empire          | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| Farcaster (tokenless) | `fid373666`                                  |
| Custom tokenless      | e.g. `glonkybota1b2c3`                       |

The vault address is always **`treasuryAddress`** / **`empireAddress`** in this API.


# Get Leaderboard By Empire

Retrieves all addresses on a specific Empire leaderboard with their initial token balances, applied boosts, final score, and ranking.

```jsx
curl "https://www.empirebuilder.world/api/leaderboard/EMPIRE_TOKEN_ADDRESS"
```

#### Example Response

```jsx
{
  "holders": [
    {
      "address": "0x7f6bec66a7512979a3eb634c53ef9b89fb71a4d0",
      "balance": "39000000057061034016445569",
      "baseBalance": "6000000008778620617914703",
      "appliedBoosts": [
        {
          "boosterId": "default-booster",
          "multiplier": 1.5,
          "type": "ERC20",
          "contractAddress": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b"
        },
        {
          "boosterId": "c1f140f6-65e9-4814-a6bd-c6adb3ee5b01",
          "multiplier": 3,
          "type": "ERC20",
          "contractAddress": "0x20dd04c17afd5c9a8b3f2cdacaa8ee7907385bef"
        },
        {
          "boosterId": "f133be60-e616-40b0-92e3-a5a7ccb5ca58",
          "multiplier": 2,
          "type": "NFT",
          "contractAddress": "0xbfd0f82949ba5b2469fdd755cc99073c442361d1"
        }
      ],
      "finalMultiplier": 6.5,
      "isLP": false,
      "farcasterUsername": "diviflyy",
      "rank": 1
    },
    {
      "address": "0x5cf457f5dd336c5c1c3ec61f81e920d9d77a8d34",
      "balance": "20628256287424012567956612",
      "baseBalance": "4584056952760891681768136",
      "appliedBoosts": [
        {
          "boosterId": "default-booster",
          "multiplier": 1.5,
          "type": "ERC20",
          "contractAddress": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b"
        },
        {
          "boosterId": "c1f140f6-65e9-4814-a6bd-c6adb3ee5b01",
          "multiplier": 3,
          "type": "ERC20",
          "contractAddress": "0x20dd04c17afd5c9a8b3f2cdacaa8ee7907385bef"
        }
      ],
      "finalMultiplier": 4.5,
      "isLP": false,
      "farcasterUsername": null,
      "rank": 2
    },
    {
      "address": "0x0cdfcf9f34b54f89c4327ff29f19b671557af0a9",
      "balance": "16502599697518979859686935",
      "baseBalance": "3667244377226439968819319",
      "appliedBoosts": [
        {
          "boosterId": "default-booster",
          "multiplier": 1.5,
          "type": "ERC20",
          "contractAddress": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b"
        },
        {
          "boosterId": "c1f140f6-65e9-4814-a6bd-c6adb3ee5b01",
          "multiplier": 3,
          "type": "ERC20",
          "contractAddress": "0x20dd04c17afd5c9a8b3f2cdacaa8ee7907385bef"
        }
      ],
      "finalMultiplier": 4.5,
      "isLP": false,
      "farcasterUsername": null,
      "rank": 3
    }
  ],
  "cached": true
}
```


# Get Empires

Retrieves a paginated list of Empires with different filtering options.

### Get Empire by Token Address

```jsx
curl "https://www.empirebuilder.world/api/empires/[TOKEN_ADDRESS]"
```

### Get All Empires

```jsx
curl "https://www.empirebuilder.world/api/empires"
```

#### Optional Query Parameters

| Parameter | Type   | Default | Description                               |
| --------- | ------ | ------- | ----------------------------------------- |
| `type`    | string | 'top'   | Filter type: 'top', 'native', or 'recent’ |
| `page`    | number | 1       | Page number for pagination                |
| `limit`   | number | 7       | Number of tokens returned per page        |

#### **Filter Types**

* **top**: Orders by 'over-ten' (not blacklisted/low-holders) and total distributed (descending)
* **native**: Filters for “Forged” Empires only and orders by 'over-ten' (not blacklisted/low-holders) and total distributed (descending)
* **recent**: Orders by creation date (newest first)

#### **Example Response**

```jsx
{
  "empires": [
    {
      "id": 83,
      "base_token": "0x2D57C47BC5D2432FEEEdf2c9150162A9862D3cCf",
      "name": "Dickbutt",
      "owner": "0xDF3eFAfdA19D2dF229eF3fbEC73d2EE6C5fB6d7c",
      "token_type": "clanker",
      "deployment_hash": "0x6d30b851d1efed9eb91725e0266c73e3992e6a72728f7fa861706c4ddbbcb9fb",
      "total_distributed": 5788,
      "created_at": "2025-03-03T17:53:21.557692+00:00",
      "token_symbol": "DICKBUTT",
      "token_name": "Dickbutt",
      "logo_uri": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/5708237f-1f2a-4ecf-e295-1ff94d183300/original",
      "split_address": null,
      "total_burned": 144703952,
      "co_emperors": null,
      "title": "The Dickbutt Empire",
      "treasury": 0,
      "treasury_claim": "no",
      "empire_address": "0x18e1F73Eb805ecC4e19dd449fBd95FaC1bB1052B",
      "v1": "no",
      "native": null,
      "distribution_counter": 3,
      "website_url": "https://dickbutt.site",
      "twitter_url": "https://x.com/dickbuttcto",
      "warpcast_url": "https://warpcast.com/~/channel/dickbutt",
      "hidden": false,
      "vaulted": false,
      "vaulted_amount": 0,
      "vault_unlock": null,
      "over-ten": true,
      "vibecoin": "no",
      "farcaster_name":"kevinmfer",
      "v1_empire_address":"0x95d72A6514630aE9792A407ba7be7239D59b6290"
    },
    // Additional empire tokens...
  ],
  "totalCount": 1931,
  "queryTime": 323.62,
  "page": 1,
  "itemsPerPage": 7
}
```


# Get Boosters By Empire

Retrieves the list of boosters configured for a specific Empire.

```jsx
curl "https://www.empirebuilder.world/api/boosters/EMPIRE_TOKEN_ADDRESS"
```

#### **Example Response**

```jsx
{
  "boosters": [
    {
      "id": "4d17fb53-d984-4889-b289-8ab86f844aed",
      "type": "ERC20",
      "contractAddress": "0xb9c09c06508613ef5f69ef1e1396f5acb476029c",
      "multiplier": 5,
      "requirement": {
        "minAmount": "5000000000000000000000000"
      },
      "tokenId": null,
      "token_symbol": "THE LAST DAY",
      "token_image_url": "https://i.ibb.co/Ng8SPwYC/placeholder.gif",
      "token_name": "THE LAST DAY",
      "nft_platform": null,
      "nft_standard": null,
      "tokenStandard": "zora-erc20",
      "chainId": 8453,
      "holders": []
    },
    {
      "id": "ba4907a0-e004-4e21-90eb-897b981d358a",
      "type": "NFT",
      "contractAddress": "0xb9287cefb75fb1288cee60a18ae59703fa8c2ecb",
      "multiplier": 2.5,
      "requirement": {
        "minAmount": ""
      },
      "tokenId": "57",
      "token_symbol": "PUSHING EMPIRES",
      "token_image_url": "https://i.seadn.io/s/raw/files/7011bfa24c0d8ef68fec211348bcb493.png?w=500&auto=format",
      "token_name": "Unidentified contract eb389ec0-3538-4e67-b29a-8e0263fe4458",
      "nft_platform": "rodeo",
      "nft_standard": "ERC1155",
      "tokenStandard": null,
      "chainId": 8453,
      "holders": []
    },
    {
      "id": "1f483842-5575-4408-9cbd-c4246ac46232",
      "type": "NFT",
      "contractAddress": "0x04b8aa10e1194bb8161cff748bc807920c291b57",
      "multiplier": 2,
      "requirement": {
        "minAmount": ""
      },
      "tokenId": null,
      "token_symbol": "Keep Pushin'",
      "token_image_url": "https://i.seadn.io/s/raw/files/1ecfa3038c50a0429a4a749bf8cece68.jpg?w=500&auto=format",
      "token_name": "Keep Pushin'",
      "nft_platform": "hypersub",
      "nft_standard": "ERC721",
      "hypersub_collection_id": "keep-pushin-nb6ux3zwmk8w",
      "tokenStandard": null,
      "chainId": 8453,
      "holders": []
    },
    {
      "id": "default-booster",
      "type": "ERC20",
      "contractAddress": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b",
      "multiplier": 1.5,
      "requirement": {
        "minAmount": "10000000000000000000000000"
      },
      "holders": [],
      "token_name": "GLANKER",
      "token_symbol": "GLANKER",
      "token_image_url": "/blackjpg.jpg"
    }
  ]
}
```


# Get Leaderboard Stats for Single Address within Empire

Retrieve the leaderboard score, boosters, and ranking for a single address within a specific Empire.

{% code overflow="wrap" %}

```jsx
curl -X POST "https://www.empirebuilder.world/api/personal-stats/EMPIRE_TOKEN_ADDRESS" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "address": "USER_ADDRESS"
  }'
```

{% endcode %}

#### Example Response

```jsx
{
  "balance": "646167253978525850703335530",
  "boostedBalance": "39416202492690076892903467330",
  "boost": 61,
  "rank": 3,
  "activeBoosterIds": [
    "default-booster",
    "ae6c6358-1dd2-4406-be84-acc90e6f01bf",
    "c3569dce-0b9e-4e2e-9988-189c4e4c1f07",
    "f1544bf4-a41f-498f-8b18-6ab0cdebbc5f",
    "daad05cc-4a7a-4ad0-897f-e193c2bc8be0",
    "ffc35d5b-479b-4f8b-805e-fc7adb30ec42",
    "a971041a-61a5-44f9-8ed2-c66fe4d4a27e",
    "f3bf17c5-0753-4467-a657-784d7fe9c242",
    "8ff2ae21-fb13-4870-97cb-8c96cca7942e",
    "9ebe5fb2-4a16-4ae0-b8c9-884168f136de",
    "d3acb66b-6025-4774-b4a3-651635df840c",
    "b61a6865-becd-4816-b607-8908b23b6d12",
    "1b0f0d9b-9d97-4fab-9626-d426481a2359",
    "ccd62eed-709b-4926-b58b-849045d816be",
    "a15775da-34e7-4cca-9d1f-842b9abf654a"
  ]
}
```


# Deploy Empire for Existing Token

Deploy a Empire Contract for an existing token on Base

```jsx
curl -X POST https://www.empirebuilder.world/api/deploy-empire \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "baseToken": "TOKEN_ADDRESS",
    "name": "My Empire Name",
    "owner": "USER_ADDRESS",
    "tokenType": "clanker",
    "tokenInfo": {
      "symbol": "TOKEN",
      "name": "Token Name",
      "logoURI": "https://example.com/logo.png"
    },
    "signature": "0xabcdef...",
    "message": "I am deploying an Empire for token {TOKEN_ADDRESS} with name {empireName}"
  }'
```

**Successful Response Example:**

```jsx
{
  "success": true,
  "empireAddress": "0x9876543210987654321098765432109876543210",
  "transactionHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab",
  "blockNumber": 15420851,
  "gasUsed": "2847293",
  "empire": {
    "id": "glanker-empire-uuid-123",
    "name": "Glanker",
    "baseToken": "0x33aC788bc9Ccb27e9ec558fb2bde79950A6b9d5B",
    "empireAddress": "0x9876543210987654321098765432109876543210",
    "owner": "0x1234567890123456789012345678901234567890",
    "tokenType": "clanker",
    "chainId": 8453,
    "deployedAt": "2024-05-29T14:30:22.000Z",
    "status": "active"
  }
}
```

**Error Response (Invalid Signature):**

```jsx
{
  "success": false,
  "error": "Invalid signature",
  "message": "The provided signature does not match the expected signer",
  "code": "INVALID_SIGNATURE"
}
```

**Error Response (Unauthorized Owner):**

```jsx
{
  "success": false,
  "error": "Unauthorized owner",
  "message": "Owner must be the token admin or a top 5 holder",
  "code": "UNAUTHORIZED_OWNER"
}
```

### Request Fields

* **`baseToken`** (string): The contract address of the existing token
* **`name`** (string): Display name for the empire
* **`owner`** (string): Address of the empire guardian/owner
* **`tokenType`** (string): Type of token - "clanker", "topHolder", or "moxie"
* **`tokenInfo`** (object): Token metadata including symbol, name, and logo URI
* **`signature`** (string): Valid signature of the message by the owner
* **`message`** (string): Message that was signed for authorization

### Authorization Requirements

* **Clanker tokens**: Owner must be the token admin/creator
* **Top Holder tokens**: Owner must be in the top 5 holders
* Signature must be valid and match the owner address

### Notes

* The API validates ownership requirements before deployment
* Empire contracts are deployed on Base network (chainId 8453)
* Each empire gets a unique contract address and database record


# Deploy Token with Attached Empire

Deploy a new Clanker token with an attached Empire contract in a single call

```jsx
curl -X POST https://www.empirebuilder.world/api/deploy-token-with-empire \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "name": "My Empire Token",
    "symbol": "MET",
    "imageUrl": "https://example.com/token-image.png",
    "creatorAddress": "0x1234567890123456789012345678901234567890",
    "signature": "0xabcdef...",
    "message": "I want to deploy My Empire Token",
    "vaultPercentage": 10,
    "vaultUnlockTimestamp": 1735689600
  }'
```

#### Parameters

| Field                  | Type   | Required | Description                                      |
| ---------------------- | ------ | -------- | ------------------------------------------------ |
| `name`                 | string | ✅        | Display name for the token and empire            |
| `symbol`               | string | ✅        | Token symbol (e.g., "MET")                       |
| `imageUrl`             | string | ✅        | URL to the token logo image                      |
| `creatorAddress`       | string | ✅        | Ethereum address of the token creator            |
| `signature`            | string | ✅        | Valid signature of the message by creatorAddress |
| `message`              | string | ✅        | Message that was signed for authorization        |
| `vaultPercentage`      | number | ❌        | Optional vault percentage (0-100)                |
| `vaultUnlockTimestamp` | number | ❌        | Optional Unix timestamp for vault unlock         |

#### Successful Response

{% code overflow="wrap" %}

```json
{
  "success": true,
  "token": {
    "address": "0x33aC788bc9Ccb27e9ec558fb2bde79950A6b9d5B",
    "name": "Glanker Empire Token",
    "symbol": "GLANKER",
    "logoURI": "https://cdn.example.com/degen-logo.png",
    "deploymentHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab",
    "blockNumber": 15420851,
    "gasUsed": "2847293",
    "creatorRewardsPercentage": 60,
    "vaultPercentage": 15,
    "vaultUnlockTimestamp": 1735689600
  },
  "empire": {
    "address": "0x9876543210987654321098765432109876543210",
    "name": "Glanker Empire Token",
    "deploymentHash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab",
    "blockNumber": 15420851,
    "gasUsed": "2847293",
    "owner": "0x1234567890123456789012345678901234567890",
    "chainId": 8453,
    "status": "active"
  },
  "deployedAt": "2024-05-29T14:30:22.000Z",
  "totalGasUsed": "2847293"
}
```

{% endcode %}

#### Error Responses

**Missing Parameters:**

```json
json{  "error": "Missing required parameters"}
```

**Invalid Signature:**

```json
json{  "error": "Invalid signature"}
```

**Deployment Failure:**

{% code overflow="wrap" %}

```json
json{  "error": "Token deployment failed after 3 attempts: Connection timeout"}
```

{% endcode %}

#### Key Features

1. **Dual Deployment**: Creates both a Clanker token and Empire contract in one call
2. **Signature Verification**: Validates the creator's signature before deployment
3. **Vault Configuration**: Optional token vault with customizable percentage and unlock time
4. **Creator Rewards**: Automatic 60% creator rewards allocation

#### Network Details

* **Chain**: Base Network (chainId: 8453)
* **Factory Contract**: V2 Factory for Empire deployment
* **Token Standard**: ERC20 via Clanker protocol


# Refresh Leaderboard By Empire

Updates and overwrites the scores for a specific Empire Leaderboard onchain and returns the updated rankings.

V2 contract supports up to 50 leaderboards - Each Partner API key will be assigned its own leaderboard.&#x20;

Default updates Leaderboard '0' with default leaderboard logic (token holdings x Boosters).

**Request (non-partner)**

{% code overflow="wrap" %}

```jsx
curl -X POST "https://www.empirebuilder.world/api/refresh-leaderboard/EMPIRE_TOKEN_ADDRESS" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "privateKey": "YOUR_PRIVATE_KEY"
  }'

```

{% endcode %}

\***Expect up to a 30-second response delay**

**Request (partner)**

{% code overflow="wrap" %}

```jsx
curl -X POST "https://www.empirebuilder.world/api/refresh-leaderboard/EMPIRE_TOKEN_ADDRESS" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "addresses": [
      "0xabcdef1234567890abcdef1234567890abcdef12",
      "0x1234567890abcdef1234567890abcdef12345678",
      "0x9876543210fedcba9876543210fedcba98765432"
    ],
    "scores": [
      "1500000000000000000000",
      "1200000000000000000000", 
      "800000000000000000000"
    ],
    "privateKey": "YOUR_PRIVATE_KEY"
  }'
```

{% endcode %}

#### Success Response Example (non-partner)

```jsx
{
  "holders": [
    {
      "address": "0xabcdef1234567890abcdef1234567890abcdef12",
      "baseBalance": "1000000000000000000000",
      "balance": "1500000000000000000000",
      "appliedBoosts": [
        {
          "boosterId": "default-booster",
          "multiplier": 1.5,
          "type": "ERC20",
          "contractAddress": "0x33ac788bc9ccb27e9ec558fb2bde79950a6b9d5b"
        }
      ],
      "finalMultiplier": 1.5,
      "isLP": false,
      "farcasterUsername": "Yerbear123",
      "rank": 1
    },
    // More holders...
  ],
  "complete": true
}
```

#### Success Response Example (partner)

```jsx
{
  "success": true,
  "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12",
  "status": "success",
  "data": {
    "tokenAddress": "EMPIRE_TOKEN_ADDRESS",
    "leaderboardId": 1,
    "addresses": [
      "0xabcdef1234567890abcdef1234567890abcdef12",
      "0x1234567890abcdef1234567890abcdef12345678"
    ],
    "scores": [
      "1500000000000000000000",
      "1200000000000000000000"
    ],
    "empireContract": "EMPIRE_CONTRACT_ADDRESS"
  }
}
```

#### Error Responses

* **400 Bad Request**: Invalid address format

```jsx
  {
    "error": "Invalid address format"
  }
```

* **500 Internal Server Error**: Processing error

```jsx
  {
    "error": "Failed to refresh holders",
    "details": "Error message details"
  }
```


# Contracts


# Empire Treasury (Read)

List of relevant read calls for Empire treasury contracts

Each Empire treasury contract is a [Splits SmartVault ERC-4337 smart account.](https://splits.notion.site/Product-Spec-SplitsVault-49f2fbf6196f4a2b95513a9819736212)&#x20;

The treasury address is the same on Base and Arbitrum.

***

### `owner()`

Returns the empire owner's address. The owner is the only signer authorized to submit UserOperations from this vault.

```solidity
function owner() external view returns (address);
```

***

### `getDeposit()`

Returns the current ETH deposit in the EntryPoint for this vault. The deposit is used to pay gas for UserOperations via the paymaster sponsorship system.

```solidity
function getDeposit() external view returns (uint256);
```

***

### `getNonce()`

Returns the current nonce for this vault's UserOperations. Nonces increment with each executed UserOp.

```solidity
function getNonce() external view returns (uint256);
```

Equivalent to calling `entryPoint.getNonce(vaultAddress, 0)` on the EntryPoint contract.

***

### EntryPoint Read Calls

The EntryPoint v0.7 at `0x0000000071727De22E5E9d8BAf0edAc6f37da032` exposes useful views:

#### `getNonce(sender, key)`

```solidity
function getNonce(address sender, uint192 key) external view returns (uint256 nonce);
```

Returns the current nonce for a given account and key. Pass the vault address as `sender` and `0` as `key`.

#### `balanceOf(account)`

```solidity
function balanceOf(address account) external view returns (uint256);
```

Returns the ETH deposit balance for an account in the EntryPoint.

***

### Checking Treasury Balances

The SmartVault holds ERC-20 tokens and ETH. To check balances, call standard token contracts directly:

```ts
// ERC-20 balance
const token = new ethers.Contract(tokenAddress, erc20Abi, provider);
const balance = await token.balanceOf(vaultAddress);

// ETH balance
const ethBalance = await provider.getBalance(vaultAddress);
```


# Empire Treasury (Write)

Relevant write calls for Empire Treasury contracts.

Each Empire treasury contract is a [Splits SmartVault ERC-4337 smart account.](https://splits.notion.site/Product-Spec-SplitsVault-49f2fbf6196f4a2b95513a9819736212)&#x20;

The treasury address is the same on Base and Arbitrum.

***

### Write Operations

* [Distribute Tokens](/empire-builder-docs/empire-builder-docs/contracts/empire-treasury-write/distribute-tokens) — send treasury tokens to leaderboard members via `executeBatch`
* [Burn ](/empire-builder-docs/empire-builder-docs/contracts/empire-treasury-write/burn)— reduce token supply by transferring to zero/dead address
* [Guardian Management ](/empire-builder-docs/empire-builder-docs/contracts/empire-treasury-write/guardian-management)— add and remove co-guardians

***

### UserOperation Flow (web app)

In the Empire Builder UI, SmartVault writes use the ERC-4337 model:

```
1. Build calldata (e.g. executeBatch with transfer calls)
2. Construct UserOp with the vault as sender
3. Submit to Alchemy bundler for gas estimation and paymaster sponsorship
4. Sign the UserOp hash with the owner's wallet (EIP-191)
5. Encode the signature as a V3 Signer struct
6. Submit the signed UserOp to the bundler
7. Bundler submits to EntryPoint → vault executes
```

Gas is sponsored by the Empire Builder paymaster. TX credits are consumed per operation.

***

### Direct Execution (non-batched)

For single operations, the vault also exposes:

```solidity
struct Call {
    address target;
    uint256 value;
    bytes data;
}

function execute(Call calldata call_) external payable;
```

This is equivalent to a single-item `executeBatch` but without the array overhead.


# Distribute Tokens

Distribute tokens to specified leaderboard

Treasury distributions send ERC-20s from the empire **SmartVault** to leaderboard members via one or more **`executeBatch`** calls on **Base** and **Arbitrum**.

**Empire Builder website:** the in-app flow still uses ERC-4337 UserOperations, the app’s paymaster, and sponsored gas — that path is **not** described here.

**API / custom integrations:** use **`POST /api/distribute-prepare`**, which returns the exact **`executeBatch`** arguments and encoded calldata.&#x20;

You **submit each transaction yourself** on the right chain (standard wallet `writeContract` / `eth_sendTransaction` to the vault).&#x20;

There is **no** bundler step and **no** Empire Builder paymaster. After the txs confirm, call **`POST /api/store-distribution`** with the hashes so balances and USD totals are indexed.

***

### Distribution modes

| Mode       | How amounts are calculated                   |
| ---------- | -------------------------------------------- |
| `even`     | Total amount to distribute ÷ recipient count |
| `weighted` | Proportional to each recipient’s `points`    |
| `raffle`   | Random winners weighted by `points`          |

***

### API workflow

#### 1. Pick the leaderboard

* List boards: **`GET /api/leaderboards?tokenAddress=<empire_id>`** (`tokenAddress` = **Empire ID** / `base_token`).
* Prepare with **`leaderboardId`: `"main"`** or a custom board **`id`** (UUID).

See [Get Leaderboard By Empire](/empire-builder-docs/empire-builder-docs/api/get-leaderboard-by-empire) to preview entries.

#### 2. Prepare

**`POST /api/distribute-prepare`**  — See [Prepare distribution](/empire-builder-docs/empire-builder-docs/api/authenticated/prepare-distribute)

Response includes:

| Field                                                 | Meaning                                                                                                                                                                           |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`summary.transactionCount`**                        | How many separate **`executeBatch`** txs to send (splits across chains and gas-sized batches).                                                                                    |
| **`summary.byChain`**                                 | Per-chain **`batchCount`**.                                                                                                                                                       |
| **`transactions[]`**                                  | One object per tx: **`chainId`**, **`contractAddress`** (vault), **`functionName`** (`executeBatch`), **`calls`** (the struct array), and **`data`** (full ABI-encoded calldata). |
| **`preview`**, **`recipients`**, **`balanceDetails`** | What was computed (recipients, tokens, balances).                                                                                                                                 |
| **`storeDistribution.exampleRequestFields`**          | Shape for **`POST /api/store-distribution`** after mining.                                                                                                                        |

#### 3. Submit on-chain

Each **`transactions[i]`** is a single transaction **to the vault** (the SmartVault at **`contractAddress`**), on the RPC network for **`chainId`**.

**On-chain: `executeBatch`**

The vault exposes:

```solidity
struct Call {
    address target;
    uint256 value;
    bytes data;
}

function executeBatch(Call[] calldata calls_) external payable;
```

Prepare fills **`calls`** with ERC-20 **`transfer`** payloads: **`target`** = token contract, **`value`** = `0` (no ETH in the inner call), **`data`** = encoded `transfer(recipient, amount)`. Your wallet (or script) invokes **`executeBatch(calls)`** on the vault — or sends the pre-encoded **`data`** field from the prepare response as the tx input. Attach **`value`** **`"0"`** unless you intentionally send native ETH with the vault call.

**Ordering:** If multiple items share the same **`chainId`**, execute them in **`batchIndex`** order so the vault’s state and nonces line up with how prepare split the work.

**Tooling:** Use your stack’s `writeContract` / `eth_sendTransaction` against **`contractAddress`** on the correct chain.

#### 4. Record

**`POST /api/store-distribution`** with one **`{ hash, chainId }`** per confirmed tx, plus **`empireAddress`**, **`baseToken`**, **`distributionMode`**, **`leaderboardType`**, **`leaderboardNumber`**.&#x20;

See [Store Distribution.](/empire-builder-docs/empire-builder-docs/api/authenticated/store-distribution)


# Burn

Burns specified token

### Burn Destinations

| Destination  | Address                                      |
| ------------ | -------------------------------------------- |
| Zero address | `0x0000000000000000000000000000000000000000` |
| Dead address | `0x000000000000000000000000000000000000dEaD` |

***

### Executing a Burn

Call the standard ERC-20 `transfer` function on the base token contract:

```solidity
IERC20(baseToken).transfer(0x000000000000000000000000000000000000dEaD, amount);
```

Or with ethers.js:

```ts
const token = new ethers.Contract(baseTokenAddress, erc20Abi, signer);
await token.transfer("0x000000000000000000000000000000000000dEaD", amount);
```

***

### Recording the Burn

After the burn transaction confirms, call `POST /api/store-burn` to record the event:

```bash
curl -X POST "https://empirebuilder.world/api/store-burn" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionHash": "0x...",
    "empireAddress": "0xVaultAddress",
    "chainId": 8453
  }'
```

The server decodes the Transfer events from the transaction receipt, verifies the destination is a burn address, and increments the empire's `total_burned` counter.

***

### Notes

* Burns are irreversible — tokens sent to the zero or dead address cannot be recovered.
* The `total_burned` counter in the empire record tracks cumulative burned amounts in raw token units (not USD).
* Burns can be submitted from any wallet — they do not require owner authorization.

See [Store Burn](/empire-builder-docs/empire-builder-docs/api/authenticated/store-burn) for the full API reference.


# Guardian Management

Add and remove co-guardians: extra wallets that can help run an empire.

This involves **on-chain SmartVault signers** on **Base and Arbitrum**, plus an API call to keep the Empire Builder database in sync.

***

### What is a Guardian?

| Role            | Description                                                                        |
| --------------- | ---------------------------------------------------------------------------------- |
| **Owner**       | Deployed the empire; primary wallet.                                               |
| **Co-guardian** | Additional wallet added by the owner (or, via API rules, an existing co-guardian). |

***

### On-chain: `addSigner` / `removeSigner` (Base + Arbitrum)

The SmartVault is an ERC-4337 account with a **signer list** (`getSigner`, `getSignerCount`, `addSigner`, `removeSigner`). The **owner is not the only signer** once co-guardians are added: each co-guardian is registered as an extra signer **on the vault**, on **both** chains where V3 runs (**Base `8453`** and **Arbitrum `42161`**). The vault address is the same on both networks.

In the Empire Builder **web app**, adding a guardian:

1. Encodes **`addSigner(newSigner, index)`** and runs it through the vault’s **`execute`** (self-call).
2. Submits that as a sponsored **UserOp on Base and on Arbitrum** (same logical operation on each chain).
3. After receipts succeed, calls the co-guardian sync API (path **`/api/co-emperors/...`**) with a transaction **`hash`** so Supabase **`co_emperors`** matches reality.

Removing a guardian runs **`removeSigner(index)`** the same way on **both** chains, then updates the API/DB.

So: **a co-guardian is added as a vault signer on Base and Arbitrum** (that is what the app does today).

***

### API: sync co-guardian list after on-chain success

The HTTP API does **not** replace the on-chain step. It verifies a successful **`hash`** (receipt on Base or Arbitrum), then updates **`empires.co_emperors`** (the stored list of co-guardian addresses).

Routes (segment name is historical `co-emperors` in the URL):

```
GET /api/co-emperors/[empire_id]
POST /api/co-emperors/[empire_id]
DELETE /api/co-emperors/[empire_id]
```

`[empire_id]` is the empire **`base_token`** (token `0x…`, `fid…`, or custom slug), same as other empire routes.

#### Add (after `addSigner` txs on both chains)

```bash
curl -X POST "https://empirebuilder.world/api/co-emperors/0xYourEmpireToken" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xNewCoGuardian",
    "hash": "0x...",
    "signer": "0xWalletThatSubmittedOnChain"
  }'
```

| Field     | Description                                                                 |
| --------- | --------------------------------------------------------------------------- |
| `address` | Co-guardian wallet to record                                                |
| `hash`    | A successful on-chain tx hash (receipt resolved on Base or Arbitrum)        |
| `signer`  | Caller must be **owner** or an existing **co-guardian** (API authorization) |

#### Remove (after `removeSigner` txs)

```bash
curl -X DELETE "https://empirebuilder.world/api/co-emperors/0xYourEmpireToken" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "0xCoGuardianToRemove",
    "hash": "0x...",
    "signer": "0xWalletThatSubmittedOnChain"
  }'
```

Integrations that don’t use the web UI must perform the **same on-chain operations on both chains**, then call **POST** or **DELETE** with a valid **`hash`**.

***

### Why both “DB guardians” and “vault signers”?

| Layer                  | What it enforces                                                                                                                                                                                                                          |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **SmartVault**         | UserOperations and direct vault execution are validated against the vault’s **signer list** (owner + anyone added with **`addSigner`**).                                                                                                  |
| **Empire Builder API** | Endpoints that take an EIP-191 **`signature`** over JSON typically check the recovered address against **`owner`** and **`co_emperors`** in Postgres — so off-chain actions stay aligned with who you treat as a guardian in the product. |

Those lists should match: add/remove co-guardians on-chain on **Base and Arbitrum**, then update the DB via **`/api/co-emperors/...`**.


# NFT Airdrop

Send ERC-721 or ERC-1155 assets from the caller’s wallet to recipients in one transaction.

This uses the same deployed **Airdrop** contract as ERC-20 airdrops; this page covers only the NFT entrypoints and events.

Calls are made **directly from an EOA** (or any `msg.sender` that owns and approves the NFTs) — **not** via the empire SmartVault.

***

### Addresses

| Network | Chain ID | Address                                      |
| ------- | -------- | -------------------------------------------- |
| Base    | 8453     | `0x596e5c15F0B7560acB96959F5ee377f905caa8A0` |

***

### Protocol fee

All `airdrop*` functions that transfer assets are **`payable`**. Send **`AIRDROP_FEE`** as `msg.value` (native ETH on that chain).

```solidity
function AIRDROP_FEE() external view returns (uint256);
```

Fee proceeds go to `feeRecipient()` (configurable by the contract owner).

***

### Write: `airdropNFT` (ERC-721)

One token ID per recipient: **`_tokenIds[i]`** is transferred to **`_recipients[i]`**. Arrays must be the same length.

```solidity
function airdropNFT(
    address _nft,
    uint256[] calldata _tokenIds,
    address[] calldata _recipients
) external payable;
```

**Before the call:** `setApprovalForAll(airdropContract, true)` on the ERC-721 contract (or per-token approvals covering every ID you send).

**`msg.value`:** `AIRDROP_FEE`.

***

### Write: `airdropERC1155`

Sends **one unit** of **`_tokenId`** to **each** address in **`_recipients`**.

```solidity
function airdropERC1155(
    address _nft,
    uint256 _tokenId,
    address[] calldata _recipients
) external payable;
```

**Before the call:** ERC-1155 approval for the airdrop contract to move the required balance (`setApprovalForAll` or equivalent).

**`msg.value`:** `AIRDROP_FEE`.

***

### Write: `airdropERC1155WithAmounts`

Per-recipient ERC-1155 sends: **`_tokenIds[i]`** / **`_amounts[i]`** go to **`_recipients[i]`**. Arrays must match in length.

```solidity
function airdropERC1155WithAmounts(
    address _nft,
    uint256[] calldata _tokenIds,
    uint256[] calldata _amounts,
    address[] calldata _recipients
) external payable;
```

**`msg.value`:** `AIRDROP_FEE`.

***

### Events

#### `AirdropNFT` (ERC-721)

```solidity
event AirdropNFT(
    address indexed nft,
    uint256[] tokenIds,
    address[] recipients
);
```

#### `AirdropERC1155` (ERC-1155)

```solidity
event AirdropERC1155(
    address indexed nft,
    uint256[] tokenIds,
    uint256[] amounts,
    address[] recipients
);
```


# ERC-20 Airdrop

Send ERC-20 tokens from the caller’s wallet to many recipients in one transaction.

## Airdrop Contract — ERC-20

This uses the same deployed **Airdrop** contract as NFT airdrops; this page covers only the ERC-20 entrypoints and events.

Calls are made **directly from an EOA** (or any `msg.sender` that holds and approves tokens) — **not** via the empire SmartVault.

***

### Addresses

| Network | Chain ID | Address                                      |
| ------- | -------- | -------------------------------------------- |
| Base    | 8453     | `0x596e5c15F0B7560acB96959F5ee377f905caa8A0` |

***

### Protocol fee

All `airdrop*` functions that transfer assets are **`payable`**. Send **`AIRDROP_FEE`** as `msg.value` (native ETH on that chain). Read the current fee on-chain:

```solidity
function AIRDROP_FEE() external view returns (uint256);
```

Fee proceeds go to `feeRecipient()` (configurable by the contract owner).

***

### Write: `airdropERC20`

Sends **`_amountPerRecipient`** of **`_token`** to each address in **`_recipients`**. Total ERC-20 pulled from the caller is `_amountPerRecipient * _recipients.length`.

```solidity
function airdropERC20(
    address _token,
    uint256 _amountPerRecipient,
    address[] calldata _recipients
) external payable;
```

**Before the call:** `approve(airdropContract, totalAmount)` on `_token`, where `totalAmount >= _amountPerRecipient * _recipients.length`.

**`msg.value`:** `AIRDROP_FEE` (or the value returned by `AIRDROP_FEE()` at call time).

***

### Event: `AirdropERC20`

```solidity
event AirdropERC20(
    address indexed token,
    uint256 amountPerRecipient,
    uint256 recipientsCount
);
```

***

### Related

* Airdrop Contract — NFT — ERC-721 and ERC-1155 push airdrops on the same contract
* Contracts overview


# Staking Locker

The **StakingLocker** is an immutable, single-deployment-per-chain staking contract. It stakes an arbitrary ERC-20 with a **configurable lock-up** (`lockDuration` in seconds from `0` — flexible — up to 10 years; no fixed tiers) and exposes paginated stakers + raw stake views — exactly what Empire Builder uses to power the Stakers leaderboard and STAKING boosters once a guardian flips `staking_activated` on the empire (see Activate Staking).

The contract holds the staked tokens directly. Empire's leaderboard pipelines fold each staker's on-chain stake back into their effective balance so token-holder rankings stay accurate.

***

### Addresses

<table><thead><tr><th width="121.03125">Network</th><th width="135.19921875">Chain ID</th><th>Address</th></tr></thead><tbody><tr><td>Base</td><td>8453</td><td>0x028088649924c6c277d22F79AAd8A258FF80e26A</td></tr><tr><td>Arbitrum</td><td>42161</td><td>0x2107412baA05470F159c025F688E97CBeADD34c0</td></tr></tbody></table>

***

### Lock duration

`lockDuration` is **any integer number of seconds** within `[0, MAX_LOCK_DURATION]`, where:

* `0` = flexible (no lock — can unstake at any time).
* `MAX_LOCK_DURATION` = `3650 days` = `315_360_000` seconds (10 years).

The contract accepts every value in that range — there are no fixed tiers

Use `isValidLockDuration(duration)` for an on-chain check; the helper just returns `duration <= MAX_LOCK_DURATION`.

#### Other constants worth knowing

* `MAX_STAKES_PER_USER = 100` — the per-user, per-token cap on **active** stake positions.
* `getMinimumStake(token)` is **set to 100** to deter dust-attack DoS.

#### Unsupported token types — do NOT stake these

The following token types **must not** be staked here — doing so may eventually leave later unstakers unable to withdraw.

* **Fee-on-transfer tokens** (e.g. tokens that take a tax on `transferFrom`). Popular examples of such tokens include SafeMoon, though such tokens are no longer commonplace.
* **Rebasing tokens** (e.g. stETH, aTokens, OHM-style rebasers). The contract's balance changes between blocks while recorded stakes don't, so positive rebases leave dust stuck and negative rebases brick later unstakes.
* **Tokens that may be paused, blocklisted, or upgraded to revert on transfer** for the contract's address.

These token types are not blocked at the contract level; integrators are expected to gate them off-chain. If you're unsure whether a token is safe, treat it as unsupported.

***

### Stake an ERC-20

A two-step flow: the caller approves `amount` on the token contract first, then calls `stake(...)` on the StakingLocker.

```solidity
// On the ERC-20:
function approve(address spender, uint256 amount) external returns (bool);

// On StakingLocker:
function stake(
    address token,
    uint256 amount,
    uint256 lockDuration
) external returns (uint256 stakeId);
```

**Required:** `amount >= getMinimumStake(token)` and `isValidLockDuration(lockDuration)` (any seconds in `[0, MAX_LOCK_DURATION]`).

```ts
import { encodeFunctionData, parseUnits } from 'viem';
import { STAKING_LOCKER_ABI } from '@/config/staking-locker';

const stakingLocker = '<STAKING_LOCKER_BASE>'; // for chainId 8453
const empireToken   = '0xDeployedErc20';
const amount        = parseUnits('1000', 18);
const lockDuration  = 7776000n; // 3 months

// 1) Approve the StakingLocker to pull the tokens
await walletClient.sendTransaction({
  to: empireToken,
  data: encodeFunctionData({
    abi: ERC20_ABI,
    functionName: 'approve',
    args: [stakingLocker, amount],
  }),
});

// 2) Stake
const txHash = await walletClient.sendTransaction({
  to: stakingLocker,
  data: encodeFunctionData({
    abi: STAKING_LOCKER_ABI,
    functionName: 'stake',
    args: [empireToken, amount, lockDuration],
  }),
});
```

The contract emits:

```solidity
event Staked(
    uint256 indexed stakeId,
    address indexed token,
    address indexed owner,
    uint256 amount,
    uint40  unlockTime,
    uint256 lockDuration
);
```

`lockDuration` is the same value passed to `stake(...)` (in seconds; `0` for flexible). It's emitted alongside `unlockTime` so subgraphs and indexers don't have to subtract `unlockTime - block.timestamp` at indexing time, and so flexible vs locked stakes can be distinguished without a follow-up read.

***

### Unstake

Unstake by the **global stake id** (not by the position in `getActiveStakes`). Use `getUserStakeIds(user, token)` to enumerate ids in the same order as `getActiveStakes`.

```solidity
function unstake(uint256 stakeId) external;
```

Reverts before `unlockTime` for non-flexible stakes. Flexible stakes (`lockDuration == 0`) can be unstaked at any time.

```ts
const stakeIds = await publicClient.readContract({
  address: stakingLocker,
  abi: STAKING_LOCKER_ABI,
  functionName: 'getUserStakeIds',
  args: [user, empireToken],
});

await walletClient.sendTransaction({
  to: stakingLocker,
  data: encodeFunctionData({
    abi: STAKING_LOCKER_ABI,
    functionName: 'unstake',
    args: [stakeIds[0]],
  }),
});
```

The contract emits:

```solidity
event Unstaked(
    uint256 indexed stakeId,
    address indexed token,
    address indexed owner,
    uint256 amount
);
```

***

### Read functions

```solidity
function getMinimumStake(address token) external pure returns (uint256);
function isValidLockDuration(uint256 duration) external pure returns (bool);

function getRawStake(address user, address token) external view returns (uint256);
function getActiveStakeCount(address user, address token) external view returns (uint256);

struct Stake {
    address token;
    address owner;
    uint40  startTime;
    uint40  unlockTime;
    bool    unstaked;
    uint256 amount;
}

function getActiveStakes(address user, address token) external view returns (Stake[] memory);
function getUserStakeIds(address user, address token) external view returns (uint256[] memory);

function getStakerCount(address token) external view returns (uint256);
function getStakersPage(address token, uint256 page)
    external view
    returns (
        address[] memory stakers,
        uint256[] memory rawStakes,
        uint256          totalStakers,
        uint256          totalPages
    );

function getLockUpRemaining(uint256 stakeId) external view returns (uint256 remaining);
function getLockUpDuration(uint256 stakeId)  external view returns (uint256 duration);

function tokenStakers(address token, uint256 index) external view returns (address);
```

Notes:

* `getStakersPage` returns **100 stakers per page**. Refresh pipelines walk `0..totalPages-1` to enumerate every staker.
* `unstaked` stakes are excluded from `getActiveStakes` already

***

#### Reading paginated stakers safely

`tokenStakers[token]` is a swap-and-pop array: when a staker fully unstakes (their last active position for that token), the tail staker is moved into the freed slot. That makes single-call iteration of `getStakersPage` cheap, but means a **multi-block walk** over `0..totalPages-1` can both **skip** and **duplicate** stakers if any full unstake lands between page reads.

Two safe options for refresh pipelines:

1. **Pin the block.** Pass an explicit `blockTag`/`blockNumber` to every `getStakersPage` call in the same walk so all pages are read at the same block. This is the simplest fix and what we recommend.
2. **De-dupe across pages.** Walk pages without pinning the block, but track the addresses you've already seen and re-read `totalStakers` / `totalPages` on every page so you notice array shrinkage.


# Empire Factory Contract

List of relevant read calls for the Empire Factory contract

**Empire Factory Contract Address:** `0x4fDB434838dcA8F48F97280570f08535Eb6155d5`

These read functions allow you to query empire information, ownership details, and retrieve filtered lists of empires based on various criteria.

***

### tokenToEmpire(address) → address

Returns the Empire contract address for a given token address.

**Parameters:**

* `token` (address): The base token contract address

**Returns:**

* Empire contract address associated with the token

**Example:**

{% code overflow="wrap" %}

```solidity
solidityaddress empireAddress = empireFactory.tokenToEmpire(0x33aC788bc9Ccb27e9ec558fb2bde79950A6b9d5B);
```

{% endcode %}

***

### empireDetails(address) → EmpireInfo

Returns detailed information about a specific Empire.

**Parameters:**

* `empire` (address): The Empire contract address

**Returns:**

* `EmpireInfo` struct containing:
  * `baseToken` (address): The underlying token address
  * `empire` (address): The Empire contract address
  * `name` (string): Empire display name
  * `guardian` (address): Guardian/owner address
  * `isActive` (bool): Whether the Empire is active
  * `fee` (uint16): Fee percentage (basis points)

**Example:**

{% code overflow="wrap" %}

```solidity
solidityEmpireInfo memory info = empireFactory.empireDetails(0x9876543210987654321098765432109876543210);
```

{% endcode %}

***

### getEmpires(address guardian, bool activeOnly) → EmpireInfo\[]

Returns an array of Empire information, optionally filtered by guardian and/or active status.

**Parameters:**

* `guardian` (address): Guardian address to filter by (use `address(0)` for all guardians)
* `activeOnly` (bool): Whether to return only active empires

**Returns:**

* Array of `EmpireInfo` structs

**Example:**

{% code overflow="wrap" %}

```solidity
solidity// Get all active empiresEmpireInfo[] memory allActiveEmpires = empireFactory.getEmpires(address(0), true);// Get all empires for a specific guardianEmpireInfo[] memory guardianEmpires = empireFactory.getEmpires(0x7f6Bec66a7512979a3Eb634c53eF9b89FB71A4d0, false);
```

{% endcode %}


