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

Investing

Bloom is a marketplace for permissioned real-world assets. Each asset is an ordinary Solidity contract deployed once on a Rome chain, and the rule for who may hold it lives inside that contract. This guide walks a buyer from the landing page through browsing an asset, checking eligibility, applying for the allowlist, buying, and holding — and then covers the one thing that is genuinely different for a holder who signs with a Solana wallet.

The rule travels with the asset. Who may hold an asset is enforced by the asset contract itself, on every transfer — not by this website. Nothing on any screen here can override it, and there is no path around it. Bloom is a window onto the chain; the chain is what decides.


What "permissioned" means for a buyer

A permissioned asset has an allowlist: a set of addresses the issuer has decided may hold it. The allowlist is not a Bloom feature — it lives on a module attached to the asset contract, and the asset consults it on every mint, burn and transfer.

The chain holds one fact about you: a single yes-or-no against your address. No documents, no personal information and no identity claims are ever stored on the chain or in this app. When an issuer admits you, all that reaches the chain is isWhitelisted(yourAddress) = true. The full compliance model is in COMPLIANCE.md.

Two consequences follow, and both are visible in the product:

  • Before you can buy, the asset is asked whether your address may hold it. A refusal is a fact about the asset's rule, not a problem with your wallet.

  • A transfer to an address the issuer has not approved reverts on chain. The asset refuses it; no screen has to.


The two wallet worlds

A single Bloom asset is reachable from both an EVM wallet (such as MetaMask) and a Solana wallet (such as Phantom). You connect one wallet at a time.

Wallet
Where it holds the asset
What signs

EVM wallet

Its own EVM address, as on any EVM chain

An EVM transaction

Solana wallet

An EVM address derived from your Solana public key

Your Solana signature

For an EVM wallet there is nothing new to learn. For a Solana wallet the asset sits at a derived address — computed as keccak256(solana_pubkey)[12..32] — and only your Solana signature can drive it. There is no bridge, no second token and no EVM account involved. This is covered in full in the Solana holder section below and in LANES.md.


The landing page

The Bloom landing page: the headline, the two wallet-world rows, and the Investor and Issuer doors.

The landing page (/) states what Bloom is — Solidity assets, open to Solana as well as EVM — and gives you two doors: Investor (which takes you to the open assets at /invest) and Issuer. As a buyer, choose Investor.

Two rows, Solana wallets and EVM wallets, list the wallets your browser has actually announced through the EIP-6963 and Wallet Standard discovery protocols. Nothing is shown that you do not have installed; each wallet's name and icon are its own.

At the foot, a Contracts section lists the addresses this deployment talks to — the token factory, the shared implementation, the permission router, the primary-sale storefront and the cash token — each linking to the block explorer, so the app never asserts an address in its own voice.

"How is it possible with Solana?"

The custody explainer: one signature enters on the left and the asset admits it on the right, with no bridge between.

The landing page carries an explainer that answers how a Solana wallet can hold a Solidity asset. It makes three points:

  • One deployment. The asset is an ordinary Solidity contract, deployed once. There is no bridged copy of it anywhere — a second copy is exactly what would let it escape the rules.

  • Two wallet worlds. An EVM wallet reaches the contract the way it reaches any chain. A Solana wallet reaches the same contract because a Rome chain runs inside Solana: the asset is held at an EVM address derived from your Solana key, and your signature is the only thing that can drive it.

  • The asset holds the rule. Who may hold it is enforced by the contract on every transfer — not by this site and not by a bridge. An address the issuer has not approved simply cannot receive it.


Browsing open assets

The open-assets list at /invest, with price and inventory read live from the chain.

/invest lists every asset with a sale open right now. For each row you see:

Column
Meaning

Asset

The asset's name and symbol, read off the token itself

Price

The price of one unit, in wUSDC

Sold

Units sold of units offered, with a progress bar

The summary above the table counts Open now (how many sales are live), Issued here (how many assets this chain has issued) and Paid in (wUSDC, the cash token for every sale on this deployment).

Nothing on this page is baked. Price, inventory and status are read live from the chain on every visit. If the chain does not answer, the page says so — "The chain did not answer…" — rather than showing a stale number or claiming emptiness. "Nothing is on sale right now" is a claim about the chain, and it appears only when the chain actually returns an empty list.

You can read prices and inventory without connecting. To buy, you will need a wallet: the page shows a Connect wallet prompt, offering your EVM and Solana wallets under two headings. Selecting a row opens that asset's detail page.


One asset

An asset detail page: the facts on the left, the eligibility card and buy panel on the right.

An asset page (/invest/[address]) has two halves: the asset's facts and rules on the left, and your eligibility and the buy panel on the right.

The facts

Fact
Meaning

Price

Price of one unit, in wUSDC

Available

Units still available in the open sale

Yield paid in

The currency any yield is distributed in (wUSDC)

Who may hold it

The page states three rules the asset enforces:

Rule
What it says

Permitted holders

Addresses approved by the issuer. Checked by the asset on every transfer.

Minimum

1 unit. No lock-up.

Transfers

Only to another permitted address, from either wallet world.

The asset checks this itself on every transfer. Nothing on this page can override it. The rules are stated as facts about the token, not as this app's policy, because that is what they are.


Eligibility — checked on the token, before any buy

The eligibility card resolving on the asset page: connect, then checking, then permitted or not permitted.

Before offering you a buy, the asset page asks the asset's own allowlist module whether your address may hold it. The eligibility card shows one of these states:

State
Meaning

Connect to check

No wallet is connected yet. Connect one to run the check.

Checking…

Connected; the on-chain check has not resolved yet.

Permitted

Your address may hold this asset.

Not permitted

The asset's rule refuses your address. A link offers to apply for the allowlist.

Unavailable

The check could not be read right now (not a refusal).

The check is two-sided. A buy moves units from the storefront to you, so both parties to that transfer must be on the allowlist. Bloom asks the question that way — a one-sided check would call you eligible and then watch the buy revert because the storefront is not listed on that token.

Once your address is on the allowlist, the same card reads Permitted, and the buy is open to you:

The eligibility card reading Permitted — the buyer is allowlisted, and the buy panel is live.

When you are refused, the reason shown is the token's own, quoted verbatim. Bloom never writes a sentence about why you were refused. If the asset has no allowlist module linked yet (a draft), it is ungated — nothing is restricting anyone, and you are permitted.

If you are refused, the card links to Apply for the allowlist.


Applying for the allowlist

The apply form: an address, a jurisdiction, a holder type, and two declarations — no documents.

/apply is where you ask an issuer to admit your address. Its lede says the whole story: No documents. Declarations only.

The form holds nothing worth leaking, and writes nothing to any chain. An application is an off-chain record the issuer decides on. The issuer runs their own checks in their own system; the allowlist write happens on their side, and all that ever reaches the chain is the yes-or-no outcome against your address.

You provide four things and no fifth:

Field
Notes

The address that will hold it

Prefilled from your connected wallet. For a Solana wallet this is your derived address — the address the asset sits at, not the key that signs for it.

Jurisdiction

One of a fixed list (Singapore, United Arab Emirates, Switzerland, United Kingdom).

You are

Individual, Accredited individual, or Institution.

Two declarations

Both must be accepted: that you act for yourself and control the address, and that you accept the issuer decides who may hold the asset and may revoke it.

The ? next to the lede explains why no documents are asked for: the issuer needs only what they will act on, nothing you could upload could be stored here, and on chain the record is a single yes or no.

To apply:

  1. Confirm the address, pick your jurisdiction and holder type, and accept both declarations.

  2. Press Submit application. The form validates locally and the store re-checks everything on the server, since a form is one request away from being bypassed.

  3. You are given a private link of the form …/apply/status?ref=AP-XXXX-XXXX-XXXX.

Save the link — it is the only way back to your application. The reference is minted from 60 bits of cryptographic randomness and is the authorisation: nothing else stands in front of your declarations, and there is no account or password to recover them with.

On the demo, a Test wiring note points you at /approve, where you can approve your own application to complete the loop. A real issuer does this step in their own system — see below.


Your application status, and erasing declarations

The status page — the reference, the declared facts, the timeline, and the erase control.

/apply/status reads an application back from its reference (from the ?ref= link, or the last one this browser remembers). There is no session; the reference is the key.

The page shows where you stand — With the issuer, You may hold this asset, or Refused — along with a details table (reference, date applied, the address, the asset, jurisdiction, and a Checks row) and a short timeline of what has happened. If you have been approved, a Buy the asset link appears. A refusal shows the issuer's own reason, quoted.

The Checks row reflects the issuer's own KYC record. On this demo there is no provider wired in, so it reads "Declarations only — no documents were asked for" rather than implying a verification that did not happen.

Erasing what you declared

You can erase your declarations at any time, from this page.

Erasing destroys the declarations and leaves the decision. What you said about yourself is deleted and cannot be recovered. The issuer's decision stays, because it has already been acted on in public, on a chain — and unsaying that is a signature the issuer must make, not a delete you can. (An issuer's own KYC record, if any, also survives, because it is the issuer's evidence for admitting an address that is now on a public chain; erasing it here would not erase anything at their provider either.)


/approve — the test-wiring stand-in

The approve page — the "test wiring, not the issuer" banner, the address, the approve action, and its on-chain requirements in the fold below it.

/approve exists so the whole journey can be walked end to end on the demo. It carries a banner reading "Test wiring — not the issuer", and that banner is the reason it is honest to have it: in production the issuer approves applications inside their own workflow, not here.

What is real about it: the approval is a genuine on-chain allowlist write. The page calls a deployed TestApprover contract that is armed on the demo asset (Mineral Vault I, ARCV, at 0x2b13ba16a429bf630f94882e477fe775d34b7c74). That contract needs no role from its caller — anyone can approve any address through it — which is the entire reason it exists: it lets the demo loop close for any wallet, from either lane.

To approve your own address:

  1. Connect the wallet you applied with (EVM or Solana). Your address is prefilled.

  2. Press Approve this address. The write's preconditions and consequences fold below the button — open "What this does" to see exactly what will happen before you sign.

  3. On success the page reads "Approved. The applicant can hold the asset now."

If you signed in with a Solana wallet, a note reads "This will approve the address derived from your Solana key" — the address the run signs for is the derived one; the key it signs with is your Solana key.

In production this is the issuer's decision, made in the issuer's system. The demo's TestApprover is open by construction so the loop is walkable; a real allowlist write is gated by a role the issuer holds. Nothing about the compliance model changes — it is still the issuer's call, still one boolean on chain, and still no documents anywhere.

Once approved, return to the asset page; your eligibility now reads Permitted.


Buying

The buy panel sits under the eligibility card on the asset page.

Need wUSDC first? Every sale is priced in wUSDC, and a fresh wallet has none. Getting funds to Rome walks the cash-in: bridge USDC or ETH in from Sepolia, then wrap the bridged gas into wUSDC — all on the /bridge screen.

The buy panel after a purchase lands — "Bought. It will show in Positions."
  1. Enter the number of Units you want. The panel shows the unit price and the total you will pay, in wUSDC. (The cash the buy actually pulls is computed from the price in base units, so the amount approved always matches the amount quoted.)

  2. Press the buy control. A buy is a short sequence, not a single click: if the sale does not yet have permission to take your wUSDC, an approval step appears first ("Allow the sale to take your cash"), then the Buy itself.

  3. Every step leads with its button and folds its preconditions and consequences below it — one click away before you sign — and names its phase as it runs — "Waiting for your wallet…", then "Landing on chain…". The sequence re-checks conditions between steps, so a sale that closes after your approval stops the buy rather than reverting it.

  4. On success the panel reads "Bought. It will show in Positions."

If your wallet is on the wrong network, the panel says so and offers to switch to the asset's chain rather than letting a button do nothing.

The buy passes through the same allowlist gate as any transfer. The storefront moves units to you only if both it and your address are permitted. There is no Bloom-side check that could substitute for the asset's own.


Your positions

/portfolio shows what you hold. Connect the wallet that holds the asset and you get:

The positions page — the holding, its value, and the summary.
  • a summary — Total value (in wUSDC), Assets (how many distinct assets you hold), and Yield received;

  • a Holdings table listing each asset, your units and their value.

Everything is read live from the chain. When you hold nothing yet, the page says so and links you to the open sales. Yield received currently shows a dash: reading your payout history means walking the distribution events back to the asset's first block, which is work still to come — a dash is honest where a 0.00 would be a claim.

Positions are a view, not a market. Bloom surfaces buying an asset from its primary sale and holding it. The asset itself permits transfers to other permitted addresses from either wallet world (that is one of its stated rules), but Bloom does not currently expose a secondary transfer or sell control.


For a Solana holder

Everything above applies to you unchanged. This section covers the one surprise that is genuinely yours: the asset is held at an address your Solana wallet will never list, and why that is safe.

The custody chain

The Solana holder's positions — "Held for your Solana key", the derived holding address, the custody chain, and the ARCV holding bought on the Solana lane.

When you connect a Solana wallet, /portfolio shows a Held at line with the EVM address your asset sits at, and a How is this held? toggle that opens a custody panel explaining the derivation in three steps:

  1. Your Solana key — the key already in your wallet. It signs; it never leaves.

  2. Computes to — the address that holds the asset — the derived EVM address. It is arithmetic, not an account: it follows from your key, and there is no private key for it.

  3. What can move it — only a signature from your key can move what sits at that address.

Why this is safe. The asset is EVM-state-resident: its balance is storage inside the Rome program's accounts. There is no SPL mint of it, no token account, and nothing Solana-native to move — so the Solana-side plumbing has no instruction that could move it out. Only your Solana signature can drive the derived address, and every movement it can express is a call into the asset contract, which runs the allowlist gate. A transfer to a non-permitted address reverts identically whether it is signed by an EVM key or a Solana key. This is exactly the property a permissioned asset wants — see LANES.md.

Because there is no SPL token to list, your Solana wallet will not display the RWA holding — the Bloom app is your positions surface. A lost Solana key is recovered through the issuer's process, not by moving the asset out-of-band.

Paying, and the extra signature prompts

  • You pay in lamports from your own wallet. The transaction fee is paid by your Solana wallet; the derived EVM address never needs to be funded.

  • Each action is one atomic Solana transaction. A gated transfer, an approval, a buy — each is a whole transaction, signed on its own. A buy is an approve then a buy, so it is two signatures in total on the Solana lane.

  • Account-heavy actions can add prompts. Most flows fit inside a single Solana transaction (a storefront buy resolves to 22 accounts and about 1,061 bytes today, inside the 1,232-byte limit). A state that touches more — for example a first-time holder whose token account still has to be created — can cross that limit, and the client then falls back to a larger transaction format built over a lookup table it creates for the purpose. Each such step is its own wallet prompt, so one buy can become three or four signatures. Your wallet will ask each time; approving each is expected.

The Solana lane is atomic only. Every Bloom flow is one EVM transaction executed inside one Solana transaction. A "multi-step journey" (approve, then buy) is several whole transactions, each atomic on its own — not a single execution split across transactions. If a call would not fit atomically, the fix is a smaller call, not a different lane.


Where the compliance really is

To summarise what is enforced where, so nothing here is mistaken for a Bloom policy:

Concern
Enforced by
Not by

Who may hold the asset

The asset's allowlist module, on every transfer

This website

A transfer to a non-approved address

Reverted on chain by the asset

Any off-chain filter

What is stored about you

One boolean (isWhitelisted) per address

Documents or PII, anywhere

Who may admit you

The issuer's off-chain decision

Bloom

For the full model, read COMPLIANCE.md; for the two-lane mechanics, LANES.md.

This is not a mock-up. A real buyer bought this asset from an EVM wallet, and a real Solana wallet bought it on the Solana lane — the same token, the same allowlist, the same wUSDC. Every transaction is linked on the Evidence page. The Permitted card, the positions, and the Solana custody chain in this guide are those buyers, after those transactions.

Last updated

Was this helpful?