For the complete documentation index, see llms.txt. This page is also available as Markdown.

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; 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 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.

/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.

/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.

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.

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.

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.

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 signaturebatchAddToWhitelist — 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.

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.

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.

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.

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.

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; for the two-lane (EVM and Solana) mechanics your holders use, read LANES.md.

Last updated

Was this helpful?