> For the complete documentation index, see [llms.txt](https://docs.rome.builders/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rome.builders/apps-on-rome/bloom/issuing.md).

# Issuing

Bloom lets you issue a **permissioned real-world asset** as an ordinary Solidity contract on a Rome chain, and administer it from a per-asset console. The asset is reachable from both EVM and Solana wallets, but **issuing is EVM-only** — a Solana wallet holds assets; it does not create them. This guide covers the issuer console, the creation wizard, the console sign-in gate, and each of the six administration tabs.

> **You do not hand Bloom any documents or personal data, and neither does your investor.** Bloom records the *outcome* of your compliance decision; the chain records one boolean per address. Your KYC/AML process — your vendors, your files, your retention — stays entirely in your own system. The full model is in [COMPLIANCE.md](/apps-on-rome/bloom/compliance.md); this guide points to it rather than restating it.

***

## What you can do

As an asset's administrator, everything below is yours to do from the console — and every one of these is a real on-chain transaction, linked on the [Evidence](/apps-on-rome/bloom/evidence.md) page:

* **Issue an asset** — the wizard deploys it as three contracts (below).
* **Review applications** — approve or refuse each one (a refusal carries a reason the applicant is owed), and admit an approved address to the allowlist.
* **Manage the allowlist** — add one address, add **a batch of addresses in a single transaction** (`batchAddToWhitelist`), or remove an address; the token enforces the list on every transfer.
* **Gate the asset** — restrict transfers to the allowlist (which makes supply final), or lift the restriction.
* **Manage supply** — read the total, and mint or burn once you hold the role.
* **Run the sale** — open a sale (units and price), close it, recall unsold inventory, and withdraw the proceeds.
* **Set and distribute yield** — set the payout currency, then distribute a payout, which walks holders one transaction at a time; excluded holders are skipped.
* **Manage roles** — grant a role to an address, revoke one, or renounce your own.

Each is detailed below, with what the chain enforces, in the tab it belongs to.

***

## The issuer console

![The issuer console at /issuer, listing the assets this deployment knows and their sale state.](/files/1OtEOzrNNgQ8qhQtHqka)

`/issuer` lists the assets you have issued on this chain and lets you start a new one.

* Connect an **EVM wallet**. A disconnected visitor, or a Solana wallet, is shown a prompt to connect the right kind of wallet rather than an empty console — a Solana wallet cannot issue, and that is a product boundary the chain enforces as a second, independent guard.
* The table lists each asset with its **name and symbol**, its lifecycle **phase**, and its **Sale** state (an *Open* badge or *Closed*).

Two columns — **Holders** and **To review** — show a dash. The holder count needs the allowlist's enumeration and the review queue is an off-chain store you sign into per asset; a dash says *not asked* where a `0` would claim a fact the app has not read.

Press **Issue an asset** to open the wizard, or select a row to open that asset's console.

***

## Creating an asset: the wizard

![The create wizard at /create: the asset fields, then six cards that light up as their transactions land.](/files/oSqAlZADmeZJYCYPdN0q)

`/create` collects four fields and then deploys your asset.

| Field        | Rule                              |
| ------------ | --------------------------------- |
| **Name**     | Required.                         |
| **Symbol**   | Required.                         |
| **Supply**   | A whole number greater than zero. |
| **Decimals** | A whole number from 0 to 18.      |

Press **Start**. If a field is invalid, the wizard tells you which and why rather than sitting behind a disabled button. When the draft is valid, your wallet is asked to sign each step in turn.

### Why it deploys piecewise

> **A token is deployed as three contracts across several transactions — never one.** Arc's one-transaction `createToken` would need **77 account locks**, and Rome caps a single transaction at **62 locks**. So the wizard deploys each piece in its own transaction and gains factory status afterwards via `ArcTokenFactoryV2.registerToken`. This is a Rome constraint made visible, not an optional optimisation — and the guards preserve Arc's upstream security model exactly (canonical proxy, whitelisted implementation, caller holds the token's admin role, registration is once-only). See [COMPLIANCE.md](/apps-on-rome/bloom/compliance.md#what-rome-adds--and-what-registertoken-preserves).

### The six cards

The wizard shows six cards, which follow the deployment's progress. A card turns from **Waiting** to **Signing…** to **Done** (or **Failed**) as its transaction lands.

| # | Card                        | What it does                                                                                             |
| - | --------------------------- | -------------------------------------------------------------------------------------------------------- |
| 1 | **Implementation**          | The shared asset implementation for this chain — resolved, nothing to sign. (Done before any step runs.) |
| 2 | **The asset**               | Deploys your asset and sets its name, symbol, supply and decimals in one step.                           |
| 3 | **Who may hold it**         | Deploys this asset's own permission module, with you as its administrator.                               |
| 4 | **Enforce it on transfers** | Points the asset at that module, so every transfer is checked by the asset itself.                       |
| 5 | **Yield rules**             | Deploys and links the module that decides who is paid.                                                   |
| 6 | **Registration**            | Registers the asset — this is what opens the sale and lets you upgrade it later.                         |

The six cards fold eight underlying deployment steps, so a single **Start** results in **eight signatures**. A note under the button reads *"Your wallet will ask you to sign each step. Stop at any point and pick up where you left off — nothing is lost."* Registration being the final step is why a stop is safe: the asset is a real deployment at each stage, registered at the end.

When the run completes, the wizard reads *"Issued. Your asset is live at …"* with a link to the asset's console at `/token/[address]`.

> **`initialize` grants you every role.** The wizard driver — your EVM address — receives the token's admin, minter/burner, yield-manager/distributor and upgrader roles, plus the allowlist module's admin role. Your key custody is therefore part of the asset's compliance posture: a lost investor wallet is recoverable only through your admin action, and the console's **Rights** tab makes every power's holder visible. (`UPGRADER_ROLE` is *not* granted to the factory automatically — grant it explicitly if you want factory-mediated upgrades.)

***

## The per-asset console, and the sign-in gate

![The console sign-in gate: sign a message with your wallet — there is no password.](/files/zA7s0RRSWWZXTqy9HlKK)

Opening `/token/[address]` does **not** show the console straight away. It shows a **sign-in gate**:

> *"Sign in to manage this asset. Sign a message with your wallet — there is no password. Your authority comes from the asset itself, and it is checked again on every action, so revoking it takes effect at once."*

Press **Sign in with your wallet**. Here is exactly what happens:

1. Your wallet is asked to `personal_sign` a short, human-readable statement (headed *"Bloom — prove you manage this asset"*) that names the one action it authorises — reading this asset's application queue — along with the asset address, the chain id, and a timestamp.
2. The app sends that signed statement to `GET /api/applications`.
3. The **server** verifies it: it recovers your address from the signature, confirms the statement is for this chain and still fresh (it authorises for a two-minute window, not a session), reads the **token's own allowlist module** from the chain, and checks that your address holds the role that the allowlist write actually requires — `MANAGER_ROLE` on that module.
4. If you hold it, the queue is returned and the console renders. If you do not, the server refuses with `403` and a reason.

![The gate showing a quoted refusal: a wallet that does not manage this asset is turned away.](/files/KJs1I1gOeHi0NmNa28V1)

> **The asset decides who may administer it — not this app.** The role is checked on the allowlist **module**, not on the token, and holding a same-named role on the token does *not* carry over — they are different authorities. There is no account and no session: every request that follows carries its own fresh signature, which is what *"checked again on every action"* means. Revoke a wallet's role on chain and it stops being able to sign in immediately.

A refusal shows the reason **quoted** — Bloom never writes a sentence about why a wallet was refused. If you decline the signature in your wallet, that is your *"no"*, not the asset's, and it is not reported as a refusal.

> **The gate is whose-console-it-is, not what-is-secret.** Supply, sale state, the allowlist roster and the roles are public chain reads any explorer will answer. What the signature buys is the **application queue** — other people's declarations — and the right to be shown the console at all.

### The console header

Once signed in, the header names the asset and shows its **phase** — the most consequential fact it has:

| Phase     | Meaning                                                                                                               |
| --------- | --------------------------------------------------------------------------------------------------------------------- |
| **Draft** | No permission module is linked yet, so nothing restricts who may hold this.                                           |
| **Open**  | Transfers are open and supply can still change. Gating the asset makes its supply final, and that cannot be undone.   |
| **Gated** | Only allowlisted addresses may hold this, **and its supply is final** — no more can be minted or burned, permanently. |

Above the tabs, a summary shows four figures — **Holders · To review · Sold · Yield paid**. Two are read from the chain and the store (*To review* once you have signed in; *Sold* from the storefront); *Holders* waits on the yield preview's holder count, and *Yield paid* waits on the payout-history cursor still to come. Each waiting cell shows a dash and explains what it needs, rather than a `0` that would claim a fact nobody asked the chain for.

***

## The six tabs

The console has six tabs, in this order: **Applications · Who may hold it · Supply · Sale · Yield · Rights.**

Every write on these tabs uses one shared control that renders the write's **named preconditions** and its **consequences** *before* the button, and names its phase as it runs (*"Waiting for your wallet…"*, *"Landing on chain…"*). Nothing should revert to teach you what could have been checked first.

### Applications — the review queue

![The Applications tab — the review queue, with a pending application to approve or refuse, and the compliance seam.](/files/5UMZ6FFOw6y6uDmCSwGR)

This tab lists who has applied to hold the asset. Each row shows the applicant's address, jurisdiction, holder type, a **Checks** column (*Passed* if your provider recorded a result, else *Declared*), and a **Status** (*To review*, *Approved*, *Refused*). Expand a row to see its facts and two controls: **Approve — may hold it** and **Refuse**. A refusal requires a reason, which becomes the applicant's answer.

> **Approving is two separate acts, and the tab keeps them honest.** Pressing **Approve** *records* your decision in the application store. Putting the address on the asset's allowlist on chain is a **second signature** — `batchAddToWhitelist` — and until it lands the row says so. When the recorded decision and the chain disagree, the tab names the gap and offers the write that closes it: **Admit** to add an approved address to the allowlist, or (for a refused address still listed) the control to remove it. The address cannot hold the asset until the allowlist write lands.

A standing note on this tab — the **compliance seam** — states where your own process plugs in:

> *"No documents are asked for or held here — the checks are your own. Connect your provider behind the `Applications` interface, or import decisions you have already made. Bloom records the outcome and the chain records one yes or no against the address."*

That is the interface a self-hosting issuer replaces: swap the application store's implementation for your own durable one and no caller changes. Bloom performs no checks and holds no document.

### Who may hold it — the transfers gate

![The Who-may-hold-it tab — the allowlist count and the restrict-transfers control, showing its one-way-door consequences.](/files/LWApEN1jTBNkzlyJGYZi)

This tab reports how many addresses are allowlisted on the asset, and lets you flip whether the allowlist is **enforced**:

* When transfers are **unrestricted**, it offers to **restrict transfers to the allowlist**. The write's consequences state that doing so makes supply final and is a one-way door for non-admins.
* When transfers are **restricted**, it offers to **lift the restriction**. Its consequences state plainly that this is not an undo.

On an *ungated* (draft) asset with no permission module linked, the tab says so — *"No permission module is linked, so nothing restricts who may hold this"* — and the toggle does not render.

> **The token checks both sides of every transfer.** While the asset is gated, a transfer where either party is off the allowlist reverts on chain with a typed error. The same gate runs for an EVM-wallet transfer, a Solana-signed transfer, a storefront sale, and the initial liquidity move — there is no path around it short of your own admin powers.

### Supply

![The Supply tab — total units in existence.](/files/uRyVONH2NdR8qXWyLuyD)

This tab reports the total supply in existence, in whole units.

### Sale — open, close, and take proceeds

![The Sale tab — an open sale, with the close-sale, recall-unsold and withdraw-proceeds controls.](/files/DLqrwrezfaPCoERM7DBG)

This tab runs the primary sale through the storefront contract.

* **When no sale is open**, it offers to open one: enter **Units to sell** and **Price per unit** (in `wUSDC`), then press **Open the sale**. The write's preconditions are shown first — the token must be registered, the storefront must hold the sale inventory, and the storefront must itself be allowlisted on the token it sells — so a sale that is not yet openable says exactly what it needs.
* **When a sale is open**, it shows how many units are left of the offer and offers **Close this sale**.
* **Recall unsold units** moves unsold inventory from the storefront back to a recipient (defaulting to you). This is **not** a close — the sale stays enabled, and the write's consequence says so, so a recall cannot be misread as an unwind.
* **Withdraw proceeds** sends the storefront's takings to an address (defaulting to you). Proceeds accrue independently of the sale's state, so they can be withdrawn whether the sale is open or closed.

### Yield — pay holders, one per transaction

![The Yield tab — the distribution amount field, with the reminder to set a payout currency first.](/files/G7fyFss04bG7CUXgB9y1)

This tab distributes a yield payout to holders.

> **A distribution is a walk, not a click.** On Rome, `distributeYieldWithLimit` pays **one holder per transaction** (the account cap), so a payout is a sequence of windows, each its own signature. You enter the amount once, in whole units of the payout currency, and it is locked once the walk begins. The **first** window pulls the whole payout from you and pays the first holder; each later window only moves money already inside the token contract. Bloom re-reads the cursor from the chain after every window — the holder set reorders as balances change, and sending a stale index would restart at zero and pull your money a second time.

The tab shows progress — *"Paying holder N of M"* — and finishes when the cursor wraps. If the asset has no payout currency set, the tab says so; setting the payout currency is the one field of a distribution only you can choose.

> **Restricted holders' shares stay in the contract.** An address excluded by the yield-blacklist module is skipped, and its pro-rata share remains in the token contract rather than being paid out.

Past payouts are not shown yet — reading them walks the distribution events back to the asset's first block, which needs a history cursor still to come. The tab says this inside itself rather than showing a `0`.

### Rights — the roles

![The Rights tab — every role and whether it is held, and the grant / revoke / renounce control.](/files/gRZZ7IWQnH1gkebZNFpX)

This tab lists every role the token declares and whether it is held, each read with a single `hasRole` call against the token. A role that is **not** held shows as such rather than being omitted — on a rights tab, a missing row and a *"no"* are different facts.

Below the list, a control changes the roles:

| Action                     | Acts on                                                            | Authority            |
| -------------------------- | ------------------------------------------------------------------ | -------------------- |
| **Grant to an account**    | An address you enter                                               | `DEFAULT_ADMIN_ROLE` |
| **Revoke from an account** | An address you enter                                               | `DEFAULT_ADMIN_ROLE` |
| **Renounce your own**      | Yourself (OpenZeppelin requires the confirmation to be the caller) | You                  |

The role choices are the token's own, so the control can never offer a role the token does not have. As with every write, the authority row and the consequence render before the button, so the door and its one-way-ness are stated before you sign.

***

## What never touches Bloom or the chain

To be unambiguous about where compliance lives:

| Thing                                | Where it lives                                                                         |
| ------------------------------------ | -------------------------------------------------------------------------------------- |
| KYC/AML documents and PII            | Your own system, with your own vendors and retention — **never** this app or the chain |
| Your decision to admit an address    | An off-chain record; you own the store                                                 |
| The on-chain effect of that decision | One boolean — `isWhitelisted(address)` — written by `batchAddToWhitelist`              |
| Enforcement of the rule              | The asset contract, on every transfer                                                  |
| Who may administer the asset         | The role held on the asset's allowlist module, checked on every request                |

For the compliance model in full, read [COMPLIANCE.md](/apps-on-rome/bloom/compliance.md); for the two-lane (EVM and Solana) mechanics your holders use, read [LANES.md](/apps-on-rome/bloom/lanes.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.rome.builders/apps-on-rome/bloom/issuing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
