# Welcome

Private securities recorded as Ledger Entry Tokens, anchored in each issuer's governing law, and the MetaLeX apps that run on them.

cyberCORPs is MetaLeX's smart-contract protocol for keeping a company's securities onchain, together with the apps built on it. A company joins as a **cyberCORP**: its governing documents designate the protocol's contracts as the register for the shares they tokenize, and those contracts issue its securities, record transfers, run fundraising rounds and settle trades. For those shares the onchain record is the register itself, and the tokens do not point at a register kept somewhere else.

The protocol works for any entity type and jurisdiction. Delaware C-corp stock is the most complete implementation, and LLC membership interests, LP interests, segregated portfolio company shares and non-US equity use the same contracts. It runs on Ethereum, Base and Arbitrum.

## Core terms

* A **cyberCORP** is the company's onchain presence: its `CyberCorp` contract plus the managers that issue its securities, run its rounds and settle its deals.
* A **Ledger Entry Token (LET)** is an ERC-721 token that records one entry on the register: who holds how many units of which class or series, on what terms and restrictions. Each class or series has its own **LET contract**.
* **Scrip** is a fungible ERC-20 token that tracks a LET's units in a fixed ratio. A holder moves units from a LET into scrip and can convert scrip back into a LET. The rights scrip carries come from the company's governing documents and the scrip's terms: under MetaLeX-form bylaws, scrip is not stock and gives no stockholder rights on its own.
* **cyberSign** records agreements in the `CyberAgreementRegistry`. When a party signs directly (`signContract`, `signContractFor`), the registry verifies the party's EIP-712 signature. When an agreement's finalizer, such as a DealManager, submits a signature it holds in escrow (`signContractWithEscrow`), the registry checks only that the finalizer sent the call. Rounds, deals and trades are signed there.

[Ledger Entry Tokens and scrip](/how-cybercorps-works/lets-and-scrip) explains how the two forms relate, and the [glossary](/building-on-the-protocol/reference/glossary) defines the rest.

## How this book is organized

| Part                         | Written for                                                                | Start with                                                                                            |
| ---------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Using the apps**           | Founders, officers, investors and holders using the MetaLeX apps. No code. | [Getting started](/using-the-apps/webapp)                                                             |
| **How cyberCORPs works**     | Anyone evaluating the model: lawyers, investors, integrators.              | [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer)                |
| **Building on the protocol** | Developers integrating with or extending the contracts.                    | [Guides](/building-on-the-protocol/how-to), then the [reference](/building-on-the-protocol/reference) |

## What runs on the protocol

| Product            | What it does                                                                                           | Guide                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| **cyberCORPs app** | Form a company or bring an existing one onchain, then run its cap table, board, grants and securities. | [Run your company](/using-the-apps/company)                                     |
| **cyberRAISE**     | Primary fundraising rounds: SAFEs, SAFTs, SAFTEs, token warrants and priced equity.                    | [cyberRAISE](/using-the-apps/cyberraise)                                        |
| **ACE**            | Rounds paid in a token community's own token.                                                          | [ACE](/using-the-apps/ace)                                                      |
| **cyberSign**      | Propose and sign agreements, each signature recorded onchain.                                          | [cyberSign](/using-the-apps/cybersign)                                          |
| **LeXcheX**        | Accredited-investor and compliance credentials attached to a wallet.                                   | [LeXcheX](/using-the-apps/lexchex)                                              |
| **cyberTRADE**     | Settlement of negotiated secondary trades under an elected exemption pathway.                          | [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade) |
| **LiquiLeX**       | Uniswap v4 liquidity pools for scrip.                                                                  | [Deploy a LiquiLeX pool](/building-on-the-protocol/how-to/deploy-liquilex-pool) |

## Source code

The contracts are in [MetaLex-Tech/cybercorps-contracts](https://github.com/MetaLex-Tech/cybercorps-contracts), and the apps in [MetaLex-Tech/metalex-webapp](https://github.com/MetaLex-Tech/metalex-webapp).


# Getting started

Who the MetaLeX apps are for, how to sign in, which app does what, and what it costs

These guides are for the people who use the MetaLeX apps: founders, officers and directors running a company, investors in its rounds, employees and other holders of its securities, and token projects forming an entity through a launchpad. They describe what you click and what each step does, with no code.

The apps run on the cyberCORPs protocol. To learn how the protocol works, read [How cyberCORPs works](/how-cybercorps-works/explanation); to build on it, start with the [guides](/building-on-the-protocol/how-to) and the [reference](/building-on-the-protocol/reference).

## Which app does what

The apps run on separate subdomains and share one MetaLeX account. A company and the securities it issues appear in every app once they exist.

| App                                           | Address                      | What you do there                                                                                                                                                       |
| --------------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [**cyberCORPs app**](/using-the-apps/company) | `cybercorps.metalex.tech`    | Form a new LLC or C-Corp, or bring an existing company onchain. Run its cap table, board, grants and documents, and issue its securities from the **Tokenization Hub**. |
| [**cyberRAISE**](/using-the-apps/cyberraise)  | `cyberraise.metalex.tech`    | Create and run fundraising rounds, and invest in them.                                                                                                                  |
| [**ACE**](/using-the-apps/ace)                | inside cyberRAISE            | Raise from a token community with a SAFE priced in the community token. Links to `ace.metalex.tech` redirect to cyberRAISE.                                             |
| [**cyberSign**](/using-the-apps/cybersign)    | `app.metalex.tech/cybersign` | Propose, review, sign and countersign agreements. The cyberCORPs app's **cyberSign** sidebar item opens it.                                                             |
| [**LeXcheX**](/using-the-apps/lexchex)        | `lexchex.metalex.tech`       | Prove accredited-investor status and hold the credential in your wallet.                                                                                                |
| [**Profile**](/using-the-apps/profile)        | `profile.metalex.tech`       | Edit your MetaLeX identity, manage linked wallets, turn on notifications and delegate signing for a Safe.                                                               |

## Find the right guide

| Guide                                                                                | Read it to                                                                                                                                    |
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| [Form a company](/using-the-apps/formation)                                          | Form an LLC or C-Corp through the app, pay for it, and follow the filing to completion.                                                       |
| [Run your company](/using-the-apps/company)                                          | Bring an existing company onchain, use mission control, the Incorporation Hub and Documents, read the public company page, and upgrade to v5. |
| [Tokenization Hub](/using-the-apps/company/tokenization-hub)                         | Set up classes and series, issue Ledger Entry Tokens (LETs), and enable scrip.                                                                |
| [The cap table](/using-the-apps/company/captable)                                    | See tokenized and untokenized positions together, import a cap table and tokenize positions.                                                  |
| [Cap-table records, modeling and compliance](/using-the-apps/company/captable-tools) | Export §219 stockholder lists, keep 409A, Rule 701, 3921 and 83(b) records, model a round and run an exit waterfall.                          |
| [Grants](/using-the-apps/company/grants)                                             | Award options, RSUs and restricted stock that vest onchain.                                                                                   |
| [boardRoom](/using-the-apps/company/boardroom)                                       | Manage officers and directors, governance documents and board consents.                                                                       |
| [cyberSign](/using-the-apps/cybersign)                                               | Propose and sign agreements, and find them in your Contract Library.                                                                          |
| [cyberRAISE](/using-the-apps/cyberraise)                                             | Run a raise as an issuer, or invest in one.                                                                                                   |
| [ACE](/using-the-apps/ace)                                                           | Run or join a token-community raise, and bridge tokens between Solana and Base.                                                               |
| [For holders](/using-the-apps/holders)                                               | See what you hold, transfer it, and convert between LETs and scrip.                                                                           |
| [Profile](/using-the-apps/profile)                                                   | Set up your identity, link wallets and delegate signing.                                                                                      |
| [LeXcheX](/using-the-apps/lexchex)                                                   | Get accredited.                                                                                                                               |
| [Launchpads](/using-the-apps/launchpads)                                             | Form the entity a MetaDAO or Umia launch prescribes.                                                                                          |

## Sign in and set up your account

Signing in the first time creates your MetaLeX account and its profile record, and links the wallet you signed in with. There is no separate registration. Your profile belongs to the account, so it follows you across every wallet you link; you add, switch and unlink wallets in **Wallet Settings** (see [Profile](/using-the-apps/profile)). If you sign in without a wallet of your own, the account gets an embedded wallet that MetaLeX creates as part of your profile.

Signing in is a free Sign-In With Ethereum message and costs no gas. Screens that show private data, such as a company workspace or your holdings, ask you to **Authenticate** when you are not signed in.

You also need:

* **A wallet.** A browser wallet such as MetaMask or Rabby works. A Safe multisig is supported and recommended for a company's treasury, and a Safe can delegate its agreement signing to one wallet (see [Profile](/using-the-apps/profile)).
* **ETH for gas** on the company's network. cyberCORPs run on Ethereum, Arbitrum and Base. A company formed through the app gets its onchain record on Ethereum mainnet, the chain the formation fee is paid on.
* **A desktop browser** for company setup and the longer flows. The apps also work on a phone.

A company's pages open for any signed-in profile that has one of the company's owner wallets linked, whichever wallet you are browsing with. Signing for the company still needs an owner wallet connected on the company's network, and the app tells you which wallet to switch to (see [Run your company](/using-the-apps/company)).

## Signatures and transactions

Your wallet asks you to approve two kinds of action, and it shows which kind before you approve:

* **A message signature** is free and instant. Signing in, agreeing to a legal document and expressing interest in a round are signatures.
* **A transaction** costs gas and confirms in a few seconds. Deploying a company, issuing a security, funding a round and closing a round are transactions.

Each guide says which steps are which.

## What it costs

* **Forming a new company** through the app is a flat **$1,000, paid in USDC**. It covers the state filing, the formation documents, the initial tax filings and a lawyer consultation (see [Form a company](/using-the-apps/formation)). A later annual report for that company costs the state's fee only.
* **Bringing an existing company onchain** is free apart from gas.
* **Issuing and managing securities** by hand in the Tokenization Hub is free apart from gas.
* **Signing with cyberSign** is free apart from gas.
* **cyberRAISE** charges the issuer **0.3% of the funds it claims** from a round. Investors pay nothing, and rejected bids are never charged.
* **Launchpad formation** costs the founder nothing: MetaLeX submits the transaction and pays its gas.

MetaLeX never takes custody of your funds or your securities. Money in transit during a raise or a deal sits in an onchain escrow that no one can override (see [Upgrades and control](/how-cybercorps-works/upgrades-and-control)).

## Terms used in these guides

* A **cyberCORP** is your company's onchain record and the contracts that run it.
* A **Ledger Entry Token (LET)** is an ERC-721 token for one ledger entry: a share position, a SAFE, an option. Each class or series has its own **LET contract** that mints its LETs.
* **Scrip** is a fungible ERC-20 token that tracks a class's LET units, which holders can trade. Its rights come from the company's governing documents; under MetaLeX-form bylaws, scrip is not stock on its own.
* The **cap table** is the record the app assembles from LETs, scrip and the positions you record offchain.
* The **securities ledger** is the company's legally definitive record. The company's governing documents, such as its bylaws, decide which record that is; tokenizing a class does not decide it.
* An **EOI** (expression of interest) is an investor's signed offer to invest in a round.

The [Glossary](/building-on-the-protocol/reference/glossary) defines the rest.

## How the apps use the protocol

The apps are front ends over the cyberCORPs smart contracts. Creating a company, issuing and transferring securities, and signing agreements are contract calls. The app also keeps offchain records, such as untokenized cap table positions, drafts, formation details and versions of legal terms, and each guide says which actions are which.

* **Deploying a cyberCORP** calls the protocol's factory, which deploys the company's contracts. When the company's governing documents designate the onchain record as its securities ledger, the chain is that ledger (see [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer)).
* **Issuing a security** mints a LET from the class's LET contract.
* **Enabling scrip** for a class deploys its scrip token, and holders can then convert LETs to scrip and back (see [LETs and scrip](/how-cybercorps-works/lets-and-scrip)).

**Under the hood** boxes in each guide name the contract behind the action you are taking. You can use the apps without reading them. To see the whole path at the contract level, follow [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp).


# Form a company

Form an LLC or incorporate a C-Corp in the cyberCORPs app for a flat $1,000 in USDC, then keep its filings, mail and annual reports in one place

If your company does not exist as a legal entity yet, the cyberCORPs app (`cybercorps.metalex.tech`) can form it for you. **Form a company** forms an LLC or incorporates a C-Corporation in one guided, in-app flow, and creates the company's public onchain record (its cyberCORP) as part of the same process.

To start, choose **Set up your company** on the app's start screen, answer **No** to **Has your company already been legally formed?**, and choose **Form a company**.

* If the company already exists, answer **Yes** and [bring it onchain](/using-the-apps/company#bring-an-existing-company-onchain) instead. That costs only the network fee.
* If a token launchpad such as MetaDAO or Umia prescribes your entity, form it from the launchpad's own flow (see [Launchpads](/using-the-apps/launchpads)). This form does not create a launchpad's vehicle.

## What the $1,000 covers

Formation costs a flat **$1,000, all inclusive, paid in USDC**. It covers:

* the state filing and the formation documents;
* filing of the initial tax documents;
* a 30-minute lawyer consultation with @lex\_node on Telegram;
* free lifetime access to the cap table features.

The whole flow runs in the app, with no sales calls. You pay on the payment chain, Ethereum mainnet, and your wallet shows the network gas separately. The form states the price only on its final review screen. Annual reports you file later through the app cost only the state's fee (see [File annual reports](#file-annual-reports)).

## Save your draft as you go

**Form a company** opens a single long-page form headed **Incorporate your company**, or **Form your LLC** once you pick an LLC. It gathers everything the filing and the onchain record need. Nothing is saved automatically. On a fresh session, a dialog tells you to save with the disk icon and to keep the session link, which reopens your saved draft in the same browser.

## Choose an entity with the entity guide

At the top of the form, an **Entity guidance** panel asks **Which entity fits your plans?** You can work through its questions (**Help me choose**), talk your plans through with GAIBE, MetaLeX's AI agent (**Talk to GAIBE first**), or skip to the form (**I already know my entity type ↓**). The questions cover where the team and the business are, how you expect to fund the company and capture value, owners and treasury, and token plans: token holdings, foreign owners, whether a launchpad will prescribe your structure and when, the proposed sale, and later token entities. Every question accepts **Not sure yet**.

**Your starting direction** then lists one or more directions. Each names the role the entity plays, explains why it fits, gives the next step and recommends a formation state. A venture-backed project is pointed at a Delaware corporation. Other answers lead to an LLC, an LLC taxed as a pass-through to hold tokens, or a US nonstock corporation, which has its own formation process outside this form. When the plan includes a launchpad that prescribes its own entity structure (the guide names MetaDAO and Umia), the direction is to follow that launchpad's onboarding. Depending on your answers, the result adds:

* **Token launch structure and timing**: a staged offshore structure (for example a Cayman foundation, with a BVI token issuer evaluated later) or the launchpad's prescribed structure, with the sources the framework relies on and a cost estimate.
* **Formation state, taxes and additional registrations**: the state recommendation, the tax consequences of the choice, and a **One-way doors** box listing tax decisions that are expensive or impossible to reverse.
* A financing card such as **Prepare your first investment** when you plan venture funding or private financing before a token sale. **Save and continue my financing plan** saves a private financing plan and continues in [cyberRAISE](/using-the-apps/cyberraise).

The guide only advises, and it labels its result "preliminary guidance … not a legal or tax opinion". The form changes only when you choose **Set this draft to {state} {C-Corp or LLC} for {role}**, which sets the entity type and state. If either one changes, the name-search and legal attestations clear and you make them again.

If your answers call for two companies (an operating corporation and a treasury LLC), **Prepare the two companies separately** saves one draft for each in this browser, listed under **My formation orders**. **Talk to the MetaLeX team ↗** opens a Telegram chat with MetaLeX.

## What the form asks for

The sections, in order:

* **Company**: the entity type (C-Corporation or LLC), the legal name and its ending, up to two optional **backup company names**, the state of formation, the **Business industry** (picked from the formation partner's list) and a **Business Description** of at most 50 characters, the limit of the IRS EIN application. Backup names are saved for manual follow-up, and automated filing submits only the reviewed primary name.
* **Responsible party** (mostly private): the IRS responsible party who signs the tax paperwork, with the option to also list them as the first public officer of the onchain record.
* **Capitalization** (always private): for a C-Corp, the authorized share count and par value; for an LLC, the member list, with ownership totalling 100%. The share count can be at most 2,147,483,647, the formation service's limit.
* **Officer roles**: President, Secretary, Treasurer and Director, each defaulting to the responsible party and each optionally listed publicly.
* **Proposed initial ownership** (always private): proposed Common Stock allocations, saved **encrypted** as a private setup plan. The form states that this creates no positions, reserves no shares and promises no stock. It pre-stages your cap table for later.
* **Public company record** (always public): the founding wallet, the treasury address, the public contact and first public officer, and the optional profile. The network is locked to the payment chain, Ethereum mainnet.
* A **legal attestation** that the details are accurate and that the public values are approved for permanent onchain publication.

### Reuse your profile details

A **Reuse your MetaLeX profile** card in the **Responsible party** section can fill blank fields (legal name, email, country of residence, phone and mailing address) from the **Private formation details** saved in your [profile](/using-the-apps/profile). Nothing is copied until you choose **Reuse profile details**, and **Undo profile prefill** reverses it.

### Check the company name

For Delaware and a few other states, the form links the state's business-name database and asks you to attest **"I searched the … database and my proposed primary name appears available"**. The attestation resets if you change the name later. For Delaware, a preflight also catches names that are too long, contain non-printable characters or use approval-sensitive terms (bank, trust, insurance, university).

Search results are advisory. The state decides when it reviews the filing, and you can [replace a name it rejects](#replace-a-rejected-company-name).

## Review and pay

**Review incorporation** (**Review LLC formation** for an LLC) opens a **Final review** that groups everything by disclosure (private filing details, the private setup plan and the public record) and states the service price: $1,000 in USDC, with network gas shown separately by your wallet. **Save and continue** leads to payment.

**Pay 1,000 USDC and start incorporation** (**… and start LLC formation** for an LLC) asks for two approvals that settle as **one onchain transaction**:

1. a gasless payment authorization, a free typed-data signature that moves no funds by itself;
2. a company-setup transaction that pays the $1,000 USDC and creates your public company record.

The state filing goes to MetaLeX's formation partner, doola, only after both are verified.

> **Under the hood.** The payment and the company-record deployment are one Multicall3 batch: a USDC `transferWithAuthorization` plus the factory's `deployCyberCorp`, which execute or revert together. Anyone can submit a signed payment authorization, so if yours is mined on its own before your batch, the fee (which only ever pays MetaLeX) transfers there and the batch reverts on the used nonce without deploying. The status page's recovery actions (verify transaction hash, reset payment attempt) reconcile exactly this case. See [Integrate from a frontend](/building-on-the-protocol/how-to/integrate-from-frontend).

## Track the order on the Formation Status page

The **Formation Status** page tracks the order. Its **Company formation** checklist runs from request accepted through state filing, required signatures, company incorporated (or LLC formed) and EIN issued, to documents available. The page also holds the signing sessions for IRS Forms SS-4 and 8821, with **Create fresh signing session**, and authenticated downloads of your private legal documents. It refreshes itself while anything is in flight.

**My formation orders**, in the profile dropdown, lists every formation you have running.

## Continue in the Incorporation Hub and mission control

Once the public company record exists and your profile holds an owner wallet, the status page opens the [Incorporation Hub](/using-the-apps/company#incorporation-hub) for you. It keeps you on the status page while payment, the state submission or replacement names are pending.

The rest of the setup arrives as tasks in [mission control](/using-the-apps/company#mission-control):

* publish your selected officers to the onchain roster from the [boardRoom](/using-the-apps/company/boardroom);
* review the **draft** cap table positions created from your private setup plan, which are visible but count toward nothing (no stock is issued);
* onboard stakeholders who have no wallet yet through invitation links;
* finish each issuance from the cap table's **Securities status** queue by reviewing the cash terms, the federal exemption and the board approval, then recording it.

Cap table setup opens as soon as the state filing completes, before the EIN arrives. You can discard a saved setup plan you no longer want at any time without touching the company or the cap table. The seeded positions and the Securities status queue are covered in [the cap table](/using-the-apps/company/captable).

## Read mail, notices and filings from doola

For a company formed through the app, doola provides legal notices, mail scans and filing records. They appear in the private **Mail, notices & filings** panel, in the [Incorporation Hub](/using-the-apps/company#incorporation-hub) and in the [Documents](/using-the-apps/company#documents) area. The panel is searchable and filterable by type, and each item has **Open / download ↗**. Only the account that submitted the formation sees it.

## File annual reports

The Incorporation Hub of a company formed through the app has an **Annual reports** panel, under **Ongoing compliance**, showing the next calendar deadline, the last filing and the state's filing requirement. Mission control also shows notices such as **Annual report due in N days** or **Annual report overdue** that link to the panel.

1. **Prepare {year} report** opens the report form.
2. **Prepare report and fix fee** saves the report and locks in the state fee.
3. Under **Payment and filing confirmation**, pay that fee with **Pay $X state fee** (in USDC on Ethereum mainnet, with no MetaLeX service fee), tick the authorization, and choose **Confirm filing with doola**.

A report with no state fee needs only the confirmation. A confirmed report cannot be replaced.

## Fix a failed filing

A failed formation shows **Formation needs attention** with the reason, on the status page and in the Incorporation Hub. Until doola accepts the order, **Edit saved details** reopens the form so you can fix what was rejected (a business description over 50 characters, for example). Then choose **Review corrections** and **Save and resubmit filing**. The company name, entity type, state, share terms and officer selections stay as recorded, and your payment is reused. **Retry with saved details** resubmits without changes.

## Replace a rejected company name

If the state rejects every submitted company name, the status page shows **New company names needed** and lists the names already rejected. Enter a first-choice replacement and up to two more, then choose **Submit replacement names**.

The public company record never changes automatically. Once the state files under a new name, a notice shows the difference, and **Update legal name with wallet →** opens the [company profile editor](/using-the-apps/company#edit-the-company-profile), where you change the onchain name with a transaction.


# Run your company

Bring an existing company onchain and run it in the cyberCORPs app: mission control, the Incorporation Hub, Documents, the public company page and v5 upgrades

The cyberCORPs app (`cybercorps.metalex.tech`) is where founders and officers run a company once it has a cyberCORP, its public onchain company record. With a company selected, a sidebar on the left lists its areas:

* **mission control** shows what is true about the company now and what needs action.
* **Incorporation Hub** holds the onchain formation record and founding documents.
* **Documents** collects formation mail, notices and filings, with links to boardRoom and cyberSign.
* **boardRoom** manages officers, governance documents and board approvals.
* **capTable** shows tokenized and untokenized positions in one cap table.
* **grants** manages equity awards (options, RSUs, restricted stock) with onchain vesting.
* **cyberRaise** opens your raises in [cyberRAISE](/using-the-apps/cyberraise).
* **Tokenization Hub** configures, issues and manages the company's LETs and scrip.
* **cyberSign** opens MetaLeX's signing app, where you sign and countersign the company's agreements.

Hovering a sidebar item shows a short description of the area. The larger areas have their own guides: [the boardRoom](/using-the-apps/company/boardroom), [the cap table](/using-the-apps/company/captable) and its [records, modeling and compliance tools](/using-the-apps/company/captable-tools), [grants](/using-the-apps/company/grants), the [Tokenization Hub](/using-the-apps/company/tokenization-hub) and [cyberSign](/using-the-apps/cybersign). Fundraising rounds are created and run in [cyberRAISE](/using-the-apps/cyberraise).

Stakeholders join through invitation links from the cap table's **Invitations** panel. Claiming one attaches the stakeholder's wallet to their record and opens **My holdings**, where they see their own positions, Ledger Entry Tokens (LETs), documents and grants. [For holders](/using-the-apps/holders) covers that side of the app.

## Bring an existing company onchain

**Set up your company** on the app's start screen leads to one question: **Has your company already been legally formed?** (as a Delaware corporation, an LLC and so on).

* **Yes**: **Set up existing company on MetaLeX** brings the company onchain free of charge, apart from the network fee. One wallet transaction creates its public onchain company record, through the wizard below.
* **No**: **Form a company** forms an LLC or C-Corp for a flat $1,000 in USDC and creates the onchain record as part of the process. See [Form a company](/using-the-apps/formation).

The setup wizard has three steps and saves your progress as you go.

![Step 1 of the wizard: Network & Treasury Setup](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-1ce6b720276a9134962147c85f34b98381c6700b%2Fcybercorps-create-network.png?alt=media)

1. **Network**: under **Network & Treasury Setup**, choose the chain (Ethereum, Arbitrum or Base) and the *payable address* that receives payments for the company. A Safe multisig is recommended, and an auto-fill button can insert your connected wallet.
2. **Legal Identity**: the **Founder identity** gives the founder or officer's name and title. It is always public, and the founder's address holds the company's primary admin authority. **Identity** gives the cyberCORP name, a primary contact (Telegram, X, email or phone), the legal entity type and the jurisdiction of formation.
3. **Public Profile**: a description, a profile image, and website, whitepaper and document links.

**Deploy cyberCORP** sends the onchain transaction. When it confirms, the company exists onchain.

> For the onchain records to be the company's securities ledger, the company's governing documents must designate the onchain system as that ledger. MetaLeX provides templates for this. Involve your counsel.

> **Under the hood.** **Deploy cyberCORP** calls the protocol's [`CyberCorpFactory`](/building-on-the-protocol/reference/factories), which deploys your [`CyberCorp`](/building-on-the-protocol/reference/contracts/cybercorp) contract, its issuance, deal and round managers, and a `BorgAuth` access-control contract in one transaction. The founder identity address becomes an officer in [BorgAuth](/building-on-the-protocol/reference/access-control). The contract-level walkthrough is [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp), and [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer) explains how the chain can be the securities ledger.

To add another company later, use **Form a new company** in the Incorporation Hub, or **Setup your cyberCORP** in the cyberCORP selector, which restarts the onboarding choice.

## mission control

**mission control** shows the live company record: a compact identity strip at the top and, below it, an **Operations** dashboard built from the same data as the cap table. A badge counts the open tasks and jumps to the **Tasks and reminders** card, which collects what needs action:

* **Contract upgrades.** When any of the company's contracts differs from the reference implementation its factory publishes, the first task reads **Contract upgrade available** (or **N contract upgrades available**), names the contracts and opens the Upgrade page. This is how an existing company's owners learn that a newer protocol release, such as v5, is available to them. If the company runs v5 and older contracts side by side, the task reads **Finish the v5 upgrade** and shows as a blocking task, because deals, rounds and issuance are paused until the upgrade is complete. If the app cannot run the upgrade for the company, the task reads **Contract upgrade needs MetaLeX** and gives the reason. See [Upgrade an existing company to v5](#upgrade-an-existing-company-to-v5).
* **LET contract migrations.** After the upgrade, **Migrate N LET contracts after the v5 upgrade** stays open until each upgraded LET contract has its holder counters, legal-owner index and holder tally, and **Set acquisition and issue dates of pre-upgrade lots** stays open until those dates are set. Both open the Upgrade page.
* **Formation steps.** A formation in progress surfaces here (payment, filing, signatures, replacement company names, officer publishing and setup steps) until it is complete. See [Form a company](/using-the-apps/formation).
* **Annual reports.** For a company formed through the app, notices such as **Annual report due in N days** or **Annual report overdue** link to the [annual reports](/using-the-apps/formation#file-annual-reports) panel.
* **Cap table and compliance.** Failed cap table checks, draft securities waiting, valuation (FMV) and 83(b) reminders, project-token setup, and stakeholders not yet onboarded.

If a live check cannot be read, the list starts with **Some live checks are unavailable**. Reload before relying on the dashboard, because the app never treats unreadable data as clear.

A brand-new company, with no stakeholders or positions, first gets a grid of setup cards: **Start or manage a cyberRaise** (which opens [cyberRAISE](/using-the-apps/cyberraise)), **Open Tokenization Hub**, **Manage your capTable** and **Enter the boardRoom**. On smaller screens, where the sidebar is hidden, the grid adds cards for the Incorporation Hub, grants and cyberSign and doubles as the app's navigation. Once the company has a record, those destinations live in the sidebar and mission control leads with the record itself.

## Search the app

The **Search** button in the header, or Ctrl+K / ⌘K, searches the app's pages and, for a company your profile owns, its stakeholders and cap table positions.

## Incorporation Hub

The **Incorporation Hub** is the company's formation record. For a company formed through the app, it combines the private state filing with the public onchain record. For any other company, it shows the public record and the founding documents. Its header links to **My formation orders** and **Form a new company**. It holds:

* **Company identity**: the legal name, entity type and jurisdiction from the formation order.
* **State formation record** (private, for a company formed through the app): the overall status, the state filing status, the filing date and number, the EIN, and any **Required signatures** (IRS Form SS-4 or 8821, each with **Review and sign**). Failure and name notices from the formation appear above it (see [Fix a failed filing](/using-the-apps/formation#fix-a-failed-filing)).
* **Public company record** (onchain, public): the legal name, entity type, jurisdiction, dispute-resolution method, the date the company went onchain, the contact, the treasury (company payable) address, and the addresses of the company's contracts (cyberCORP, BorgAuth, and the issuance, deal and round managers), each linked to a block explorer.
* A read-only officer roster with a **Manage officers in boardRoom →** hand-off. Officers are added, removed and published in the [boardRoom](/using-the-apps/company/boardroom).
* **Mail, notices & filings** (private), for a company formed through the app. See [Read mail, notices and filings from doola](/using-the-apps/formation#read-mail-notices-and-filings-from-doola).
* **Annual reports**, under **Ongoing compliance**, for a company formed through the app. See [File annual reports](/using-the-apps/formation#file-annual-reports).
* **Public onchain agreements**: the agreements permanently published to the company record at formation.

A company formed through the app with no saved ownership proposal also gets a **Start your cap table** card here, which points to [the cap table](/using-the-apps/company/captable).

> **Under the hood.** The public record shows the state your deploy transaction wrote to the company's contracts. See [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp).

## Documents

The **Documents** area gathers the company's document stores in one place:

* **Mail, notices & filings**: the same private panel as in the Incorporation Hub.
* **Governance documents**: **Open governance documents** goes to the boardRoom's registry-backed [governance documents](/using-the-apps/company/boardroom#governance-documents) archive.
* **Sign with cyberSign**: **Open cyberSign ↗** opens MetaLeX's signing app. An existing agreement's own signing link opens that agreement.

The page also shows **Formation documents** and **Standalone templates** cards. Document preparation and templates are not enabled in the production app, and both cards say so. The formation card links to the Incorporation Hub for filing status and company records.

## Edit the company profile

The **edit profile** link in mission control's identity strip opens the **admin** area. Its top half is the **public profile** (description, image and links), saved without a transaction. Below it, **onchain details** lets an owner update the cyberCORP name, the payable address and the officer roster. Each of those changes is an onchain transaction sent from an owner wallet. Removing an officer whose wallet holds the company's grants authority shows a warning you must acknowledge first. The [grants guide](/using-the-apps/company/grants) explains the grants authority.

## Public company pages

Every cyberCORP has a **public company page** that anyone can read without a wallet or an account, at `cybercorps.metalex.tech/company/{chainId}/{address}`. Token holders can use it to check the entity behind a project. It shows what the chain and the public MetaLeX indexer record, and every address links to a block explorer so you can check it yourself:

* **Entity**: the entity type and jurisdiction the company recorded onchain at formation (not checked against any government registry), the formation source (the MetaDAO or Umia launchpad, or *Self-formed* through the MetaLeX cyberCORP factory), the date its contracts were deployed (the legal formation date may differ), the chain and the formation factory.
* **Contracts and deployed versions**: the CyberCorp, Issuance Manager, Deal Manager, Round Manager and BorgAuth contracts. For the CyberCorp and the three managers, the page reads each version live when it loads and compares it with the reference implementation its factory publishes (*Matches factory reference* or *Differs from factory reference*), which also shows whether the company runs v4 or v5. BorgAuth shows its address only, with *Version not read by this page*.
* **Officers**: the officer roster kept in the company's contract, with the names and titles the company recorded.
* **Security classes onchain**: each LET contract the company deployed, with its class, series, the number of LETs minted (voided ones included) and any scrip token.
* **Registered agreements**: agreements registered in the same transaction that deployed the company, which is how the launchpad factories register formation documents, each with its status, agreement ID and an **Open the document on IPFS** link. Agreements registered later are not listed.
* **Public rounds**: the company's publicly advertised rounds, marked *Scheduled*, *Open*, *Cap reached*, *Ended* or *Closed*, each with a **View round** link. Private rounds are not listed.
* **Chronology**: indexed onchain events, oldest first, with a list of what it leaves out.

A closing section, **What this page does not show**, lists the gaps: filing status or good standing with any registry; the cap table (no holders, positions or ownership percentages, and no claim about which record is the company's securities ledger, which depends on its governing documents); document contents; private rounds, deals, investors and anyone's contact details; and anything held offchain. A section that can't be read says so and shows nothing in its place.

The launchpad formation pages, their formation summaries and a company's cyberRAISE round pages link here, and a shared link to a public company page shows a preview image. Companies formed through the MetaDAO and Umia launchpads are also listed in a public directory at `cybercorps.metalex.tech/company` (see [Launchpads](/using-the-apps/launchpads)).

## Company access follows your profile

* Company pages open for a signed-in profile that has one of the company's owner wallets linked, whichever wallet you are browsing with. If yours has none, the page names the owner wallets on record so you can switch to or [link](/using-the-apps/profile) the right one.
* Signing a company transaction needs an owner wallet connected on the company's network. A notice on the page says which wallet and offers to switch.
* If the company record can't be verified, the page says **Could not load cyberCORP** and offers **Retry company record**. If a page fails to load at all, it shows **Unable to load this page** with **Try again**.
* Issuing, transferring, scripifying and approving change the company's onchain records, so each is a transaction and costs a small amount of gas. Offchain cap table entries, drafts and profile edits are plain saves.
* MetaLeX cannot issue your securities, move your funds or change your company's onchain records. [Upgrades and control](/how-cybercorps-works/upgrades-and-control) sets out what MetaLeX does and does not control.

## Upgrade an existing company to v5

A new company is created on the current protocol release, v5. An existing company keeps the contract version it was deployed with until its owners upgrade it, and the app adapts to whichever version the company runs. When an upgrade is available, mission control lists it as its first task, which opens the **Upgrade** page. The scripify form also sends you there when the Issuance Manager is out of date. You upgrade only when you choose to.

The **Upgrade** page lists the six steps of the v5 upgrade in the order the v5 release uses: **CyberCorp**, **Issuance Manager**, **Deal Manager**, **Round Manager**, then the two beacons the Issuance Manager owns, **LET contracts (CyberCertPrinter)** and **CyberScrip**. Upgrading a beacon moves every LET contract or scrip token of the company at once. Each step shows the contract, the factory it is bound to, its current implementation and the reference that factory publishes, the current and latest versions, and a status:

* **Up to date**: the contract runs the factory's reference.
* **Upgrade available**: a newer version is published. **Switch available** means the implementation differs from the reference at the same version number.
* **Shared beacon**: the contract runs shared code that the company cannot upgrade from here. The oldest cyberCORPs run their core contracts on MetaLeX-owned beacons and cannot upgrade those contracts themselves.
* **Not deployed**, **Implementation unknown** or **Reference unavailable**: nothing can be sent until the reads succeed.

The page starts a v4 to v5 upgrade only if every step can finish at that time. It refuses, with the reason, a company that has a contract older than v4, a shared-beacon contract, no Issuance Manager or Deal Manager, or a factory reference below v5. Mission control then shows **Contract upgrade needs MetaLeX**. A v4 company without a Round Manager upgrades in five steps.

To upgrade:

1. Open the **Contract upgrade available** task in mission control, or the Upgrade page, and review the versions.
2. If a Safe that your wallet owns holds the owner role on the company, choose **Propose to Safe**. The Safe receives one transaction with every step that is not done, followed by the LET contract migrations below that need no dates. The steps run in order and revert together, so the company never runs v5 and older contracts side by side. Collect the Safe signatures and execute it in the Safe app. If the same batch already waits in the Safe queue, the page shows its signature count instead of proposing it again. If a queued batch was proposed for an earlier state of the company, the page tells you to reject it.

   The batch grows with the company's holders and lots. The page estimates its gas from bounds measured on a Base fork (at most 120,000 gas for each holder counter, and 100,000 for each lot in each backfill). It does not offer a batch whose estimate is more than half of the chain's block gas limit, because the Safe may not be able to execute it. In that case, contact MetaLeX. Do not split the batch: a split reopens the period that the warning below describes.
3. Otherwise, send the steps from a wallet that holds the owner role. Only the next step's **Upgrade** button is active. Each step is its own transaction, and the page reads the chain again before each one. The LET contract migrations follow in later transactions, so read the warning about scrip conversion below before you start.
4. Choose **Refresh versions** to reread the versions afterwards.

{% hint style="warning" %}
**Finish every step in one sitting.** Contracts on different versions call functions the other version does not have. While the company runs v5 and older contracts side by side, mission control shows **Finish the v5 upgrade**, and the app refuses deals, rounds and issuance, both for officers and for investors, with a message that names the Upgrade page. Closing a round early, refunds, rejections and recalls are not refused.
{% endhint %}

{% hint style="danger" %}
**Turn off transfers, close FCFS rounds and clear recertification approvals before you upgrade.** After the LET contract beacon upgrade, each wallet that holds a pre-upgrade LET has a holder counter of zero until it is seeded. If a LET is minted or transferred to that wallet first, its counter stays below its balance. The deployed contracts cannot correct this, because `initializeHolderCount` accepts only a zero counter.

In this period the app refuses issuance, and it refuses a new recertification approval on a LET contract with pending migrations. But two calls do not need the company:

* A holder can call `convertScripToCert` on the Issuance Manager. With a recertification approval on file, it mints a new LET to the holder's wallet.
* An investor can call `submitEOI` on the Round Manager. In a first-come, first-served (FCFS) round that has not ended, the round allocates and mints a LET in the same call once the round opens.

If the LET contract allows transfers, any holder can also transfer a lot without the company. Once one holder's counter is seeded, a transfer from that holder to a holder that is not seeded yet has the same effect, whatever the seeding order.

A pending deal or allocation settles by a transfer out of the Deal Manager or Round Manager, which anyone can trigger once it is signed and paid. A transfer out of a holder whose counter is still zero reverts. So the checklist seeds those two managers last: a settlement cannot succeed until every other holder is seeded. Keep that order if you send the calls yourself.

From a wallet, the migrations run after the upgrade. In that period the company's contracts run v5, so v5-only paths open too: a holder can post a secondary offer, and a buyer can accept it and settle it with `finalizeSecondaryTradeAgreement`, which mints a LET to the buyer. The app cannot stop these direct calls, and the preparation below does not close them. A Safe batch closes the period, because it seeds every counter in the same transaction as the beacon upgrade. **If the company's LET contracts have holders from before the upgrade, upgrade through a Safe.** The page builds a Safe batch when you propose it, so a lot minted while it waits for signatures is not in it. So both ways need the same preparation:

* Turn off holder transfers on every LET contract: the contract's global flag (`setGlobalTransferable`) and each lot's own override (`setTokenTransferable`). The Upgrade page lists each one that is on, with a **Turn off** button, and holds both ways until all are off. Turn transfers on again only after the checklist shows every holder counter seeded. Until then, the checklist does not send the next counter call while transfers are on, and the app refuses to turn holder transfers on for a LET contract with pending migrations. Transfers out of the Deal and Round Managers settle deals and allocations, and the contract allows them either way.
* Clear every recertification approval on the company's LET contracts. The contracts cannot list their approvals. So the Upgrade page takes them from the app's index, up to the last block the index processed, and from the Issuance Manager's `RecertificationApprovalSet` events after that block, up to the latest block. It confirms each one onchain and shows a **Revoke** button for each. The next wallet step shows **Revoke approvals first**, and the Safe batch cannot be proposed, until none is on file.
* Close every FCFS round that has not ended, including a round that has not started yet, with **Close round now** (`closeRoundNow`). The Upgrade page finds these rounds the same way, from the index and the Round Manager's `RoundCreated` events, and shows that button for each. If an FCFS round that has not ended runs on a Round Manager the company replaced, the page cannot check or close it there, and it holds the upgrade: contact MetaLeX. The index records a Round Manager only when the factory deploys it. If the company uses a Round Manager that the index does not record, the page also holds the upgrade: contact MetaLeX. The page cannot find a round on a manager that the company set with `setRoundManager` and then replaced again. If you did this, close every FCFS round on that manager before you upgrade. Both ways stay disabled until none is left. Closing sets the round's end time to the time of that block, and the round refuses an EOI only after that time, so the page counts the round as open until a later block. A round that restricts the reduction of its end time cannot be closed early. If you have one, wait until the round ends.
* Settle or void every pending deal and secondary offer whose lots the Deal Manager holds. Anyone can settle a signed and paid deal or accept an offer, and the Deal Manager delivers even with transfers off. A delivery to a new holder after a Safe batch was built leaves that holder out of the batch, and a later delivery to it would leave its counter wrong. The page lists the escrowed lots and holds both ways until there are none.
* Do not mint or transfer LETs outside the app, and do not issue or propose deals while a Safe batch waits for signatures. Do not sign a queued Safe batch unless the Upgrade page shows it as waiting: the page rebuilds the batch from the chain and marks a queued batch that no longer matches as out of date. Execute the batch promptly after that check. The page cannot see changes between its last check and the execution.

If the index reports no progress, or is more than 10,000 blocks behind the chain, the page cannot confirm that its lists are complete, and both ways stay disabled. The lists are complete only if the index processed its blocks correctly.

Set the cleared approvals again only after the checklist shows every required migration done, so that the app no longer pauses the LET contract. The holder counters are not enough: until the legal-owner index is backfilled, a conversion with an approval also takes its units from the shared scrip vault instead of the holder's own positions. If a counter does go wrong, the checklist reports a mismatched holder counter, and the app keeps issuance, deal settlement and scrip conversion paused on that LET contract. Contact MetaLeX in that case.
{% endhint %}

### Finish the LET contract migrations

v5 adds records to each LET contract that start empty for every LET minted before the upgrade. Once the company runs v5, the Upgrade page shows a checklist for each LET contract, read directly from the contract:

* **Holder counters.** A wallet whose possession counter reads zero cannot transfer a pre-upgrade LET out, and a LET minted to it first leaves its counter wrong for good. **Run next call** seeds each counter (an owner call).
* **Legal-owner index.** Until it is backfilled, converting scrip back to a LET can fail or draw on the shared scrip vault instead of the holder's own positions.
* **LeXcheX badge.** If the company has a holder cap configured, the badge is wired before the holders are counted. The tally then reads each holder's beneficial-owner count, U.S. status and credential expiry from the badge. Otherwise wiring it is optional; after a late wiring, **Recount** counts the holders again.
* **Holder tally.** The count a holder-cap check reads, including whether a buyer already holds the class. Until it is backfilled, the check undercounts existing holders and can admit a buyer although the cap is already full.

Until the holder counters, legal-owner index and holder tally of a LET contract are complete, the app refuses issuance, deal settlement and scrip conversion on it, and mission control shows **Migrate N LET contracts after the v5 upgrade**. With a holder cap, the tally is complete only when it agrees with the badge for every counted holder. Anyone can send the index and tally backfills; the counters and the badge need the owner or admin role. Holders can still call the contract directly, which is why the Safe batch carries these calls in the same transaction as the upgrade.

The checklist also handles the dates. Missing dates do not cause the app to refuse issuance, deal settlement or scrip conversion as above. But a missing acquisition date still blocks Rule 144 and Reg S trades of that LET, so set the acquisition dates before holders need to trade:

* **Acquisition dates (Rule 144 and Reg S).** Each pre-upgrade LET's acquisition date reads zero, and Rule 144 and Reg S trades refuse it. The page proposes the time the current holder of record took the LET onchain, which is usually the same as or later than the real acquisition. On a LET contract whose extension records fund-interest acquisition dates, **Copy recorded dates** copies them first.
* **Issue dates (certificate image).** The certificate image shows a blank **Issue Date**. The page proposes the company's cap table issue date for a tokenized position, else the issuance endorsement, else the mint.

Review each date against the company's records, change any that are wrong, and confirm the table before you send. A date earlier than the onchain record is allowed, but the page flags it. The Rule 144 and Reg S conditions add their fixed period to this date, so an earlier date makes more of that period count as served, and the LET can trade sooner. From a wallet, each date is its own transaction; a Safe sends them as one. Mission control shows **Set acquisition and issue dates of pre-upgrade lots** until they are set.

Each class panel in the Tokenization Hub shows **Registered-owner transfer permission (v5)**, which starts off on an upgraded LET contract. Until it is on, changing a LET's registered owner and settling secondary trades on that class fail. Turn it on for the classes that should allow them (see [Transfer permissions](/using-the-apps/company/tokenization-hub#transfer-permissions)), after the checklist's migrations are done. The checklist shows its state and links there.

> **Under the hood.** Upgrades use a co-approval model: MetaLeX publishes a new implementation, and your company opts in, so neither side can act alone. Each contract is compared with the reference published by the factory it is bound to, which is the factory its own upgrade check reads. See [Upgrade model](/building-on-the-protocol/reference/upgrade-model) and [Upgrades and control](/how-cybercorps-works/upgrades-and-control).


# Tokenization Hub

Set up a company's security classes and series, issue Ledger Entry Tokens (LETs), enable scrip and approve de-scripification

The **Tokenization Hub** is the cyberCORPs app area where a company sets up its security classes and series, issues Ledger Entry Tokens (LETs) to holders, and manages scrip, the fungible form of a class. Like every company area, it opens for a signed-in profile that has one of the company's owner wallets linked (see [Run your company](/using-the-apps/company#company-access-follows-your-profile)).

The Hub displays and changes the company's onchain securities records. When a company's governing documents designate those records as its securities ledger (MetaLeX-form bylaws call it the Tokenized Stock Ledger System), they form part of that ledger. The app does not infer that legal status from tokenization.

Three buttons sit at the top of the Hub: **Cap Table**, **Add class or series** and **+ Issue security**.

> **Under the hood.** Each class or series has its own LET contract, a [`LedgerEntryToken`](/building-on-the-protocol/reference/contracts/ledgerentrytoken) deployment that mints ERC-721 LETs. A class with scrip also has a [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip) ERC-20. How a LET and the class's scrip relate, and what rights scrip carries, is covered in [LETs and scrip](/how-cybercorps-works/lets-and-scrip).

## Classes and series

The Hub's main view, **Classes and series**, groups the company's LET contracts under their legal class (**Legal class · …**), each with an **Add series** button. A LET contract with no recorded legal identity sits under **Unclassified or unresolved**, and a saved cap table line that has no LET contract yet offers **Configure or resume tokenization** or **Record legal identity**. Each class or series panel shows:

* its **Registered units** and status, including whether scrip is enabled;
* its **Class delivery permission**;
* if scrip is enabled, the scrip ratio, the de-scrip threshold, and how much of the class is held as LETs and how much as scrip;
* an **Ownership State** summary of active LETs and registered holders, with any it could not resolve counted separately, and the [transfer permission](#transfer-permissions) switches.

Expanding a panel adds three working sections:

* **Legal class and terms** records the legal identity behind the line and its versioned terms: **Attach legal identity**, **Propose terms version**, then a current officer's **Approve with wallet** (a free signature) or **Record external approval**, and **Activate**. The [cap table guide](/using-the-apps/company/captable) walks through the workflow under *Legal classes, series and versioned terms*.
* **Tokenize cap table units** lists the untokenized cap table positions linked to this LET contract. **Tokenize units** mints a LET for some or all of a position's units to the holder's linked wallet. It needs a v5 company and activated legal terms. The [cap table guide](/using-the-apps/company/captable) covers it under *Tokenizing part of a position*.
* **Scripify whitelist**, on classes with scrip, controls which LETs can be scripified (see [Choose who can scripify](#choose-who-can-scripify)).

## Transfer permissions

Two class-level switches control how a class's LETs move. You change either one from the class panel, and each change is an onchain transaction.

| Switch                                        | Controls                                                                 | Available on            |
| --------------------------------------------- | ------------------------------------------------------------------------ | ----------------------- |
| **Class delivery permission**                 | the class default for moving LETs between wallets                        | v4 and v5 LET contracts |
| **Registered-owner transfer permission (v5)** | changes of a LET's registered owner, including settling secondary trades | v5 LET contracts        |

On a new v5 LET contract both start off, and issuing a LET is not subject to either. A v4 LET contract has the delivery switch only. On a LET contract upgraded to v5, the registered-owner switch also starts off, and until it is on, changing a LET's registered owner and settling secondary trades on that class fail. Turn it on only after the post-upgrade steps in [Upgrade an existing company to v5](/using-the-apps/company#upgrade-an-existing-company-to-v5). Per-LET overrides and restriction hooks apply on top of both switches.

## Add a class or series

A security can be issued only once its class exists, and each class or series line gets its own LET contract. **Add class or series** walks through three steps, and only the last one is onchain:

1. **Legal class and series**: **What are you adding?** (a class without a series, or a series of a class), the legal class (new or existing), the **Legal class name** and **Legal series name**, the **Governing document reference**, the **Instrument type** (SAFE, SAFT, SAFTE, token warrant, token purchase agreement, convertible note, common stock or preferred stock) and an optional protocol series category. **Save legal identity** saves this offchain.
2. **Tokenization settings**: the **Certificate template** (for example the Reg D or Reg S variant of a SAFE), a **Token name** and **Ticker** (both prefilled), and the certificate document, uploaded as a PDF or given as a URI. **Save tokenization settings** saves them.
3. **Review and deploy**: **Deploy printer** sends the transaction that deploys the LET contract. If the wallet's result is lost, paste the **Transaction hash from your wallet** and choose **Check transaction and finish linking**.

Setting up a LET contract needs a current company officer and supported company and Issuance Manager versions on the network. v4 and v5 companies are both supported. The instrument list does not include stock options or restricted stock and token awards. A cap table line of an unsupported type shows **This cap table line cannot be configured here** and points back to the cap table.

> **Under the hood.** The company's [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager) deploys the LET contract (`createCertPrinter`) for the chosen [security type](/building-on-the-protocol/reference/security-types). A [certificate extension](/building-on-the-protocol/reference/extensions) handles the instrument-specific terms.

## Issue a LET

**+ Issue security** opens **Issue New Security**, where you mint a LET to a holder. Pick the class or series, then fill in the certificate details:

* **Investor details**: the holder's name (with profile lookup) and address.
* **Security detail**: the number of units represented, the investment amount (in the class's payment token, USDC by default) and the issuance date, which must be in the past.
* **Certificate-specific terms**: terms that depend on the security type, such as a SAFE's custom provisions or a SAFT's unlock schedule.
* **Signing officers**: the officer or officers signing the certificate.
* **Legal terms**: the dispute-resolution method and the governing legal document.

Issuing takes two approvals. The signing officer signs the certificate (a free signature), then you confirm the onchain transaction that mints the LET. To mint a LET for a position already on the cap table, use the cap table's tokenize actions instead (see [the cap table](/using-the-apps/company/captable)).

> **Under the hood.** The transaction calls the [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager), which mints the LET on the class's LET contract and records the holder as the registered owner. See [Issue a LET](/building-on-the-protocol/how-to/issue-a-let).

## Issued securities

Below the classes, the **Issued Securities** table lists each security with its active LETs, status, units represented and whether delivery is enabled. Expanding a security shows:

* its LETs, each with the registered owner (and the custody wallet, where different), wallet, ID, agreement, units, issue date and contact;
* its scrip holders.

Tick **hide VOIDed positions** to hide voided LETs. When a holder has requested de-scripification, a banner prompts you to review it. Opening a LET shows its certificate page, where the company's owners and the current holder can **Download PDF**. The holder's side of these pages is in [For holders](/using-the-apps/holders).

## Enable scrip for a class

Scripifying a class gives it a tradable, fungible form. **Scrip Configuration** asks for:

* the **scrip ratio**: how many scrip tokens equal one share. The ratio is permanent, and a confirmation step repeats the exact ratio back to you before anything is deployed.
* the **de-scrip threshold**: the minimum amount of scrip that can be converted back into a LET. You can adjust it later.
* **De-scrip Configuration**, fixed to **Founder Approval**: registered holders de-scripify automatically, and new holders need your approval. The **Auto** option is shown but cannot be selected.
* **Who can scripify**: see [Choose who can scripify](#choose-who-can-scripify).
* **clawback**: an optional, one-way **No clawback** switch that permanently disables the issuer's force-transfer, freeze and burn override for the class. Turn it on for a class whose grants promise that vested shares belong irrevocably to the recipient. Leave it off to keep the issuer override for compliance.

Scripifying needs the Issuance Manager on the latest version. Until it is, the form says "The Issuance Manager must be upgraded before scripifying." and links to the [Upgrade page](/using-the-apps/company#upgrade-an-existing-company-to-v5).

> **Under the hood.** Scripifying deploys a [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip) ERC-20 for the class through the IssuanceManager (`deployCyberScrip`). Holders can then convert LET units to scrip and back. Partial scripification, the scrip ratio and the two recertification paths are covered in [Scripify and settle a secondary trade](/building-on-the-protocol/how-to/scripify-and-settle).

## Choose who can scripify

On the Scrip Configuration screen, **Who can scripify** offers "Only whitelisted certificates can be scripified (recommended for private shares)", checked by default. The whitelist starts with the LETs the cap table links as a stock plan's reserve in this class. Without a whitelist, any holder of record can turn shares into transferable scrip, including unvested and restricted shares.

After scripifying, manage the list from the class panel's **Scripify whitelist**. **Turn on** and **Turn off** switch the restriction, and **Add** and **Remove** list or unlist each LET. For private shares the app recommends turning it on and listing only the company's reserve and FBO LETs, so vesting and restricted shares cannot become transferable scrip. Each change is an onchain transaction, on v4 and v5 companies alike. A holder who tries to scripify an unlisted LET is told that a company officer must add it first.

## Approve de-scripification

A holder who already holds a LET of the class converts scrip back without your approval. A scrip holder who is not yet a registered holder sends a de-scripification request instead, and a banner in the Hub prompts you to review it. **Approve De-scripification** pre-fills the holder and share amount, and you complete and sign the certificate details and confirm. The holder then becomes a registered holder.

Approval needs activated legal terms for the class or series (see **Legal class and terms** under [Classes and series](#classes-and-series)). Without them, the screen explains why and links back to the class in the Hub.

> **Under the hood.** Issuer approval is the step with legal effect, because it adds a new holder of record. See [LETs and scrip](/how-cybercorps-works/lets-and-scrip) and [Compliance architecture](/how-cybercorps-works/compliance-architecture).


# The cap table

One cap table for tokenized and untokenized positions, with AI-assisted import, versioned legal terms, partial tokenization and LLC records

The **capTable** item in the sidebar, or the **Cap Table** button in the [Tokenization Hub](/using-the-apps/company/tokenization-hub), opens the company's cap table. Positions tokenized as Ledger Entry Tokens (LETs), positions recorded only in the app, and beneficial holdings derived from scrip balances appear in one view and go through the same math.

> **The cap table is in beta.** The app asks you to export it regularly and keep a local copy. Exports include every position, with cancelled, terminated and tokenized history, so a saved `.xlsx` or `.csv` is a full backup.

## Who can open it

Only the company's owners can open the full cap table, for reading as well as writing: connect an owner wallet of the cyberCORP and complete the free **Authenticate** signature. Holders get their own scoped view, described in [For holders: your securities](/using-the-apps/holders).

A corporation gets the stock cap table this page describes, and an LLC gets a unit-based one (see [LLC cap tables](#llc-cap-tables)), following the legal type recorded on the cyberCORP. Any other type is refused with "Cap table workflows require an explicitly identified corporation or LLC. Review the company's legal type in its profile."

Holder names that were encrypted at issuance (see [The public LET page](/using-the-apps/holders#the-public-let-page)) are decrypted on the server while the table is assembled. A name the app cannot decrypt shows as a scrambled placeholder instead of ciphertext.

## How to read the table

Each row's source badge records where the record lives and whether the units are tokenized:

* **⛓ tokenized**: the units exist as a LET onchain, and the row mirrors it.
* **offchain**: the position is recorded and edited in the app, for untokenized units. Recording a row documents an issuance without effecting one, so the corporate authorization behind it (a board consent in [boardRoom](/using-the-apps/company/boardroom), for instance) must still exist.
* **≈ tokenized (scrips)**: a beneficial claim derived from a scrip balance. Holding scrip is an economic position, and the holder is not a registered stockholder for those units.
* **⛓ escrowed onchain**: an equity award whose scrip sits in a MetaVesT vesting escrow (see [Token grants and onchain vesting](/using-the-apps/company/grants)).

The company's governing documents, such as its bylaws, decide which record is its legally definitive securities ledger, whatever a row's badge says. Where they designate the onchain system (see [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer)), the LETs are that record because of the designation. The app never infers it from token status or storage location.

Once the company has issued scrip, a lens toggle appears. **Registered** shows record owners, the view stockholder lists and corporate law use. **Beneficial** follows the economics and moves scripified shares from the LET's owner to the scrip holders.

The **Securities cap table** tab has summary cards (issued and outstanding, semi-diluted and fully diluted shares, stakeholders, total raised, onchain certificates, offchain entries, duplicates), a donut by class, and sections for **Equity**, **Convertibles**, **Token instruments** and **History** (void, terminated, tokenized, exercised and converted rows, kept for audit). You can group by stakeholder or by class/series, base the % column on issued and outstanding, semi-diluted or fully diluted shares, and search for a holder. Per-class subtotals and each section's **Total** row follow the active lens, basis and filter. In the stakeholder grouping, a **Class/series** filter narrows the view to the classes you pick. A class line and its linked LET contract count as one class there, and row percentages keep the company-wide denominator.

The **Integrity checks** panel recomputes the table's invariants live, such as fully diluted shares accounted for, SAFE ownership under 100%, every position attached to a stakeholder, scrip pools conserving units and plan reserves backed by their LETs. Investigate a failing check before relying on the numbers.

### When the onchain source is incomplete

The app reads the onchain half from an indexer. If the read is incomplete or has not reached the block it needs, a tokenization allocation is unreconciled, or a class line carries conflicting legal identities, the app stops presenting totals. A caution banner reads "Some certificate records are incomplete. Registered exports are unavailable." and lists the gaps, the summary cards switch to **Known issued subtotal** and **Known amount raised**, the donut is hidden, and **Model a round** and **Exit waterfall** show "Analysis requires complete source quantities and terms." until the source is complete.

## Review banners

Up to four banners above the table flag records that need a decision, each with its own review dialog.

* **Review duplicates**: offchain positions that share a holder, class and unit count, usually left by a re-import. Compare them field by field, then delete the accidental copy or dismiss the group.
* **Review unregistered scrip**: plan-reserve scrip in wallets beyond what grant withdrawals explain. Flagged rows carry a **⚠ unregistered** chip, and the review can stage a draft position (never an active one) prefilled from the onchain evidence.
* **Review cert double counts**: a counting LET beside a counting live offchain entry for the same holder, class and size, as an unlink or a declined import link leaves them. The dialog names the remedy for each pair, usually **Link existing cert** on the entry or **Unlink cert** on a mis-linked position, and never assumes the LET is the mistake. You apply the fix on the rows themselves, and can dismiss pairs that are separate holdings.
* **Review cert printer links** and **Review duplicate classes** open **Reconcile classes**. A tokenized series is an offchain class line linked to its LET contract. Once linked, the pair renders as one class everywhere, and until then it counts as two. The dialog lists unlinked series lines and lines that look like one class recorded twice (same class/series, matching name), and offers a journaled **merge** into a survivor whose terms win. A line with an attached legal identity can only be the survivor. Display labels derive from the class/series, so renaming a line's stored legal name in **Class terms** is safe.

## Stakeholders

A **stakeholder** has a name, an optional email and mailing address, a relationship (founder, investor, employee and so on) and any number of linked wallets. Wallets join the two halves of the table: a LET whose holder wallet a stakeholder has linked appears under that stakeholder. A LET that matches no one shows an **unmatched wallet** badge with a one-click path to a new stakeholder, and the **Link Wallets** panel matches in bulk. A wallet belongs to one stakeholder at most.

### Invite a stakeholder to the holder portal

The **Invitations** panel generates a private onboarding link per stakeholder. The app never emails it, so you send it through a channel you trust. The invitee connects a wallet and signs in with SIWE, which attaches the wallet to their record and opens their [My holdings](/using-the-apps/holders) portal. Links expire after 14 days, and regenerating one revokes the previous link.

**Holder portal disclosures** decides whether invited holders also see the documents attached to their positions, and per-unit and exercise prices. Both are off until you turn them on.

## Record a position

**+ Add Position** records an untokenized position offchain, with no transaction. The form takes:

* the **class**, with inline creation of classes and series. A new class whose name matches an existing line of the same class/series is refused, and the form points you to that line or to a rename in **Class terms**. An equity award takes an **award type** (stock option, RSU or restricted stock) on its underlying class: a Common option is award type "Stock option" on class "Common Stock";
* the **plan pool** an award draws from, which you can create inline with its reserve;
* units, investment amount, price per unit, valuation and round label;
* **vesting** (start, end, cliff in months, termination date, vested override) and, for paid awards, the strike or repurchase price, the post-termination window and the option's term expiration;
* SAFE terms (cap basis, discount) and token-instrument terms (claim, amount, unlock schedule);
* a **federal exemption** tag (Rule 701 entries feed the [Rule 701 monitor](/using-the-apps/company/captable-tools)), transfer-restriction chips, a paper certificate reference, notes and private PDF attachments, which owners can always open and the holder can open once you turn on the position-documents disclosure.

**Save as draft** stages a position that counts toward nothing until you issue it from [Securities status](#securities-status).

**Void** and **Terminate** record real corporate events and keep the row in History. **Delete** is only for an entry that never reflected a real holding, and removes it for good.

## Import a cap table

The **Import** panel takes `.csv` and `.xlsx` sheets (blank templates are on the toolbar) and OCF (Open Cap Table Format) as a `.json` bundle or the `.zip` that Carta or Pulley export. You give each row an action before anything is written: create an offchain record, update an existing position (rows exported from this app carry a `position_id`), link to an existing LET instead of duplicating it, or skip.

Class names are canonicalized, so "Common Stock - Acme, Inc." merges into "Common Stock". When an import would add class lines beside existing ones of the same class/series, the preview warns you first. New lines are right for a new sub-series and wrong for a sheet that only spells a class differently. Blocking errors stop the import, grouped by issue so you can fix the sheet.

**GAIBE**, the AI import below the file input, converts a cap table in any form (a spreadsheet, a Carta or Pulley export, a PDF, a screenshot, pasted prose) into the import format. Limits: uploads of about 3 MB, pasted text of 400k characters, 25 documents per officer per day, and no legacy `.xls` (re-save as `.xlsx`). GAIBE leaves a field it is unsure of blank and flagged, carries numbers digit for digit, and skips summary rows and percentage columns, which the app recomputes. A **column mapping report** lists every source column, and each extracted field is shown under its preview row. Converted rows go through the same validation and per-row actions as a hand-built sheet, and nothing is imported until you commit.

## Reset the offchain records

**Reset Ledger** opens *Clear offchain cap-table records?*, which clears the removable offchain positions, stakeholders, classes and plans so you can re-import. Tokenized positions, positions tied to a live MetaVesT escrow, and the audit journal (which dated stockholder lists use) are kept. You type `RESET` to confirm, and it cannot be undone, so export first.

The reset is refused while the table holds formation-managed positions with issued history, or once it carries append-only evidence: a class line with an attached [legal identity](#legal-classes-series-and-versioned-terms) or a position with an approved tokenization source. Correct those entries by voiding, terminating or re-recording them. Clearing formation-managed drafts consumes them, and the one-time formation setup cannot be staged again.

## Tokenize a position

**Tokenize →** on a row mints one LET for the whole position, on v4 and v5 companies. To mint for part of a position, see [Tokenizing part of a position](#tokenizing-part-of-a-position-v5-companies).

The dialog prefills the certificate form from the entry, lets you pick the recipient wallet when the holder has several, and lists the cap table details that stay offchain (vesting schedule, exercise price, discount, notes and similar fields). Minting is one transaction, preceded by a board-consent check when a consent in [boardRoom](/using-the-apps/company/boardroom) covers the position, and the entry then mirrors the LET. If the transaction mines but the link-back fails, the dialog lets you retry the link or enter the LET's number, so you never mint a duplicate.

**Link existing cert** links a LET that already exists on the class's LET contract, without minting. The app checks the LET number you enter: the LET must be live, claimed by no other position, held by a wallet of this stakeholder, and carry exactly the position's registered units. The position then mirrors it, as after **Tokenize →**, and an unlinked holder wallet is recorded on the stakeholder. Use it to relink a LET unlinked from the wrong position, or after importing a table whose LETs already exist.

**Unlink cert**, the one action on a tokenized row, is a journaled correction for a mistaken link. The position becomes an active offchain record again while the LET stays live onchain, so both count and the double-count review flags the pair. Resolve it promptly with **Link existing cert** on the correct position, or void the LET onchain.

Equity awards (options, RSUs, vesting restricted stock) never become LETs: **Escrow award (MetaVesT) →** sends them to [grants](/using-the-apps/company/grants), where their scrip escrows onchain. Stock plan reserves have their own rows, and **Tokenize plan** mints one LET to the company for the full reserve, which you scripify to fund grants. A fully scripified reserve shows a "fully scripified" chip.

If the class has no LET contract, the action reads **Create cert printer →** and takes you through class creation in the [Tokenization Hub](/using-the-apps/company/tokenization-hub). If several LET contracts match the class and the line is linked to none, it reads **Choose cert printer →** and asks which one mints this class's LETs.

### Tokenizing part of a position (v5 companies)

In the [Tokenization Hub](/using-the-apps/company/tokenization-hub), a class panel's **Tokenize cap table units** lists the untokenized positions whose class line is linked to that LET contract, with each split (for example "1,000 units · 250 tokenized · 750 untokenized"). A position qualifies when it is active and offchain, is not an equity award, is not linked whole to a LET, and is not in a vesting escrow. The class line must be linked to the LET contract (the **Review cert printer links** banner does that) and must have [activated legal terms](#legal-classes-series-and-versioned-terms). Without them the attempt is refused with a message naming the line.

**Tokenize units** states how many units remain untokenized and that "The position keeps one cap table row; only its tokenized and untokenized split changes." Enter fewer than the prefilled remainder in **Number of units represented by certificate** to tokenize part of it. The holder is fixed to their linked wallet, with a **Registered owner** choice if they have several, and **Tokenize these units** sends one transaction from an owner wallet. A v4 company is refused with "This cyberCORP reports contract version 4; tokenization allocations require the cyberCORPs v5 protocol."

The app reserves the units before the wallet request, so they cannot be minted twice, then checks the chain and reports:

* **Units tokenized**: the LET carries the units, and the split updates once the indexer reaches the mint's block.
* **Units not tokenized**: nothing was minted (the request never reached your wallet, or the transaction reverted), and the units are released.
* **Units held**: the app could not establish that nothing was minted, for example after you declined the wallet request or when the outcome could not be read. Held units are neither tokenized nor released automatically. The panel shows "· N held", the integrity checks fail, and registered exports are unavailable until the allocation reconciles.

The dialog warns about held units before you confirm. The first attempt records a permanent tokenization source tying the position to the LET contract, the activated terms and the holder's wallet, after which the position cannot be deleted (**Void** and **Terminate** still work). A partially tokenized position stays one cap table row with its LETs folded in, and exports carry `tokenized_units`, `untokenized_units` and `unresolved_units`. Once you tokenize a position from the Hub, do not also use **Tokenize →** on it.

## Legal classes, series and versioned terms

The charter or other governing document defines each class, and a class line holds the app's working terms for it. Legal identity and versioned terms connect the two: you record which legal class (and series) the line is, record the exact terms text from the governing document as a numbered version, have a current officer approve that version, and activate it. The cap table, its exports and the v5 tokenization paths then read the activated terms in place of the line's stored values.

The workflow is the **Legal class and terms** section of a class panel in the [Tokenization Hub](/using-the-apps/company/tokenization-hub). It needs the LET contract linked to a cap table line. Until it is, the section says the line has no legal identity yet and links to the cap table's **Review cert printer links** banner. Reading needs a signed-in owner wallet, and the other steps need that wallet to be a current officer of the cyberCORP. No step is a transaction: identities, versions and approvals are app records, and the wallet approval is a free signature.

1. **Attach a legal identity.** Choose the **Parent legal class** (or **New legal class…**), whether **This line is** a series of it or the class itself, the series name, the **Source reference (document and clause)** and the **Exact source text**, then **Attach legal identity**. The app first verifies the line's history in the cap table journal (created or imported in the app, never changed outside it) and shows it as **Line history**. If it does not verify, the button stays disabled with "A legal identity cannot be attached until the history verifies." A plan pool cannot take an identity, and a line takes only one.
2. **Propose a terms version.** **Propose terms version** asks for the **Document reference**, **Document version**, **Exact terms text from the document**, the conversion ratio as numerator and denominator (blank means unknown, never 1:1), an optional proposed effective date ("Leave blank to take effect on approval.") and optional **Scoped rights and voting rules**, recorded verbatim and not evaluated. **Propose version** saves an immutable record in state **Proposed**.
3. **Approve it.** One current officer's approval is enough. **Approve with wallet** signs the exact version (document, version, text digest, class and series). The app verifies the signature, including from a Safe or other contract wallet, and checks onchain that the signer is a current officer. **Record external approval** instead records approvals obtained outside the app, such as a board or stockholder consent: the eligible groups, an optional stated effective date, the consent PDF and each approval, with the recording officer as approver. The version becomes **Approved, not active**.
4. **Activate.** **Activate** makes the version govern from the latest of the approval time, the approval's stated effective date and the proposed effective date, so a version can show **Activated, effective later** before it is **Active**.

To amend, propose, approve and activate a new version, which makes the previous one **Superseded**. An older version cannot be activated over a newer active one, and a proposal that changed after approval needs a new approval.

Activated terms govern:

* **Class terms**, which locks the conversion ratio and names the version, effective date and document (see [Class terms](/using-the-apps/company/captable-tools)). Without an identity or an active version, the line's stored terms govern and the form says which case applies.
* **Cap table math**, which uses the version's exact conversion ratio.
* **Exports**. The `.csv` carries `class_terms_source` (`legal_version` or `app_line`), the reason when the app line governs, and the version id and digest. The `.xlsx` adds a **Class terms** sheet, and the OCF bundle carries the same facts. A line with no active version still exports.
* **Tokenization and conversion**: [partial tokenization](#tokenizing-part-of-a-position-v5-companies) mints against the activated terms, and a holder's conversion of scrip back into a LET is checked against them (see [For holders](/using-the-apps/holders#convert-scrip-back-into-a-let)).
* **Imports**, which are refused if they set a conversion ratio that contradicts the active terms.

## Positions seeded by formation

For a company formed through the app (see [Form a company](/using-the-apps/formation)), the cap table starts with a requested **Common Stock** class awaiting review and a **Proposed initial ownership** section holding the draft positions from your private setup plan. They are badged *Proposed · not issued* and stay out of every total until each is recorded. A formed company with no saved ownership proposal gets a **Start your cap table** card in the Incorporation Hub instead.

## Securities status

**Securities status**, linked from the cap table header with a live draft count, is the officer's issuance worklist, with four queues derived live from the cap table and the chain:

1. **Drafts**: issue (as active or as "promised"), edit or delete staged positions. **Formation-managed drafts** go through **Review cash terms**, **Review exemption** and **Review board approval**, in that order, then **Record issuance**. They cannot be edited, and once recorded they cannot return to draft or be deleted, so corrections use Edit, Void and Terminate. Only plain Common Stock can be recorded this way. A draft with restricted, award, plan, convertible or token terms stays a non-counting draft for a custom workflow.
2. **Awaiting signature**: proposed onchain, unsigned by the recipient. Copy the cyberSign signing link and send it yourself, since the app sends no email.
3. **Awaiting acceptance**: signed, with the escrow not yet finalized.
4. **Awaiting settlement**: escrowed with vested units to sweep or post-termination cleanup left.

## Exports

The toolbar exports the full table, history included, as `.csv`, `.xlsx` or an OCF bundle. Each export records when it was generated, its record date if any, and the indexer block its onchain rows were read at and whether that block was final.

The `.csv` and `.xlsx` always export and note gaps beside the affected rows. The OCF bundle and the record-date stockholder list are registered exports, which the app refuses rather than build from an incomplete source. The refusal starts "Registered export unavailable:" and names the cause: the indexer has not reached the record date's block or that block is not final, a tokenization allocation is unreconciled, or a class line's legal terms evidence is unresolved. A cap table built from an earlier indexer must be regenerated first.

A current untokenized row also offers **Download statement**, a PDF position statement that says on its face it is neither a tokenized certificate nor a stock certificate and does not by itself determine which record is the company's securities ledger. A row mirroring a LET offers **Download certificate**, a PDF rendering of the indexed LET that tells the reader to verify its status onchain.

The §219 stockholder list, 409A and FMV records, the Rule 701 and Form 3921 monitors, 83(b) tracking and the scenario calculators are in [Cap table records, modeling and compliance](/using-the-apps/company/captable-tools).

> **Under the hood.** Tokenized rows are [LETs](/building-on-the-protocol/reference/contracts/ledgerentrytoken) minted through the [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager), scrip-derived rows read [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip) balances, and award escrows are MetaVesT allocations. Quantities are exact decimal strings end to end (onchain values are 18-decimal fixed point), so forms reject a 19th decimal place rather than let the escrow and the cap table drift apart through rounding. See [LETs and scrip](/how-cybercorps-works/lets-and-scrip).

## LLC cap tables

For an LLC, **capTable** opens the **LLC cap table**: ordinary capital-interest units grouped by legal class, with its own records and workflows. It has none of the stock tooling (no §219 list, no 409A, Rule 701, 3921 or 83(b) panel, no round or waterfall model, no Token cap table, no import). Tabs show **Classes**, **Positions** (the default), **Holders**, **Members** and **History**, with search, class and status filters.

* **Classes** have a **Class name**, an **Operating agreement reference** and a **Rights summary (ordinary capital interests)**.
* **Positions** are units of a class held by a stakeholder, to 18 decimal places: a draft, a recorded holding or cancelled. Only recorded holdings count. The one percentage shown is "% of recorded class units", and the page states that class-unit percentages do not determine voting or distribution rights.
* **Memberships** have admission and cessation dates with an evidence reference. They are recorded separately from units: holding units does not make someone a member, and recording an admission issues no units.

**Add draft position** stages a draft and **Record issuance** turns it into a recorded holding. **Record transfer** and **Record cancellation** act on a recorded holding. A transfer closes the source position and records the recipient, and any units left over, as linked positions in the same class. **Record admission** and **Record cessation** handle memberships. Each workflow asks for the effective date, the operating agreement version, the authority and restrictions relied on, the approvers and their capacities, the executed approval or consent and its date, and the executed instrument, all as text references. You confirm the event occurred with the required approvals, review, and choose **Confirm and record**. Recording documents an event that already happened. It does not issue units, admit a member or execute a transfer. An approval dated after the event is refused, and so is a future-dated event.

**Plan a unit allocation** is a worksheet of proposed holders, units and resulting class percentages with a JSON download ("Planning only. Nothing is saved to your cap table.").

**Tokenize LLC units** mints a LET for a whole active position on an existing v5 CommonStock LET contract with no certificate extension, used as a compatibility container. Map the class to that LET contract with the approval reference for the encoding and its public terms (**Save class mapping**), pick the position, confirm the recipient wallet recorded on the stakeholder, then **Reserve whole position** and **Sign, simulate and submit**. The app refuses unless the company, its issuance manager and the LET contract all verify as v5. Before mapping, note that the governing references, rights summaries and approval evidence become public onchain, holder names are left out, USD amount fields are zero, and the contract-generated metadata still says "CommonStock / Shares". The LET stays part of the same LLC position, and a reserved or tokenized position cannot be changed offchain until its tokenization reconciles. A reverted transaction releases the reservation, and **Release unsent reservation** releases an unsent one.

**Export this view (CSV)** uses rounded percentages, and **Export all records (JSON)** keeps exact quantities and every record and history entry regardless of filters. If the LLC had imported stock records or onchain positions before its native LLC records existed, **View legacy cap table** shows them read-only. Bulk reset is unavailable once native LLC records exist.

## Good to know

* **Offchain writes are free.** Recording, editing, importing and inviting send no transaction. Minting LETs and escrowing awards cost gas.
* **Every offchain change is journaled.** The append-only audit trail backs the as-of reconstruction behind dated stockholder lists.
* **v4 and v5 companies share most of the cap table.** The app reads each company's contract version and refuses visibly where a feature needs v5: partial tokenization, LLC tokenization and private sales (see [For holders](/using-the-apps/holders#prepare-a-private-sale-v5-companies)). Converting scrip back into a LET also works on a v4 company that runs the current v4 release of its issuance manager (see [Convert scrip back into a LET](/using-the-apps/holders#convert-scrip-back-into-a-let)). A company keeps its deployed version until its owners upgrade it (see [Run your company](/using-the-apps/company)).


# Cap table records, modeling and compliance

Stockholder lists, 409A and Rule 701 records, tax trackers, token claims and scenario modeling beside a corporation's cap table

A corporation's [cap table](/using-the-apps/company/captable) has toolbar panels and tabs for records and analysis. They are offchain and free. Each one records what you enter and does the arithmetic. None is a valuation, a filing or legal advice, and each panel says so where a legal judgment is involved. The FMV, 83(b) and position panels keep private PDFs next to the data they support, readable only by owners (and, for position documents, by the holder once you turn on that disclosure). An LLC's cap table has none of these tools (see [LLC cap tables](/using-the-apps/company/captable#llc-cap-tables)).

## DGCL §219 stockholder list

**§219 List** reconstructs the registered record holders of issued and outstanding stock as of any record date, from offchain cap table changes and the onchain history of Ledger Entry Tokens (LETs) through that date. Scrip holders are left out because scrip is not stock until it is converted back into a LET, and the panel cites the bylaws provision that says so.

Pick a record date, optionally label the snapshot ("2026 annual meeting record date"), and choose **Download list (.csv)** or **Save snapshot**. Saved snapshots are immutable audit records. As-of reconstruction resolves identities retroactively, so regenerating the same date later may not reproduce a past list exactly. Save the snapshot you used.

The list is a registered export, so the app refuses it rather than build it from an incomplete source. For the onchain half, the indexer must have processed the record date's block and that block must be final. A recent record date can be refused for a short while with "Registered export unavailable: the record date is not yet final" and the block numbers. The list is also refused while a tokenization allocation is unreconciled or a class's legal terms evidence is unresolved, and each refusal names its cause. The `.csv` records each class's terms source (activated legal terms or the app's class line), the tokenized and untokenized units of partially tokenized positions, and the block the list was read at.

## 409A and FMV records

**409A / FMV** records fair-market-value evidence the issuer provides: the provider, the FMV per common share, effective and expiration dates, and a privately stored PDF of the valuation report. Records are append-only, so a replaced valuation is marked superseded instead of edited.

The current FMV feeds option grants. The Add Position form shows a green banner when a current 409A covers a new option grant and an amber one when none does.

## Rule 701 disclosure monitor

Awards tagged with the **Rule 701** federal exemption feed a rolling monitor of the trailing and peak 12-month totals against the $10M federal disclosure threshold. Options are valued at exercise price and other awards at the issuer-recorded FMV covering the grant date. Missing data is never inferred: a "resolve before relying on the total" list names every position whose data would change the answer. The panel monitors a threshold, and it states that whether an offering qualifies for Rule 701 is a question for counsel.

## Option exercises and Form 3921

**Option Exercises / 3921** records immutable exercise facts from mined MetaVesT events (who exercised, how much, at what strike, when) and counts what a filing export would still need: grant dates, strikes and exercise-date FMV. If the recipient's browser failed to save an exercise, a repair path re-reads it from the chain by its allocation and transaction hash.

The panel has no filing export. It states that the export stays unavailable until recipient tax identity has an encrypted, purpose-limited storage and access design, and it stores no taxpayer identifier.

## 83(b) election tracker

For issued vesting restricted-stock awards, **83(b)** tracks the 30-day election window and stores the filing evidence the issuer reports: a filing date, the submission method and a required PDF. It counts pending windows, past-due awards with no evidence, and filings on record. The panel describes itself as a reminder and evidence system, never proof that the IRS accepted a filing.

## Model a round

**Model a round** converts your post-money SAFEs at a hypothetical priced round. Enter the new money, the round valuation (pre- or post-money, with helper text reminding you that this prices the round and is not a SAFE cap), and optionally a new option pool as a percentage of the post-round company. Results show the price per share, the new shares, each SAFE's conversion and whether its cap or the round price governs, and a before-and-after ownership table for existing holders, new money and the pool. Convertibles whose terms the model cannot confirm are listed instead of guessed at, and an integrity line confirms that ownership reconciles to 100%.

A **sequential rounds** variant chains several future rounds. Saved scenarios keep inputs only and recompute against the live cap table when loaded, and up to three can be compared side by side.

## Exit waterfall

**Exit waterfall** distributes a hypothetical sale value down the preference stack: debt, liquidation preferences by seniority, participation (with caps), as-converted classes, common, and options net of strike. Cumulative preferred dividends accrue to the exit date you choose. Results come per class, with each class's treatment labelled, and per stakeholder. A company with scrip chooses between the **Registered** and **Beneficial** basis, and the panel notes that Registered is the §219-valid default.

Both calculators model scenarios and write nothing to the cap table. Both need a complete source: while the cap table reports incomplete records, each tab shows "Analysis requires complete source quantities and terms." in place of a model.

## Class terms

The waterfall and the fully diluted math depend on class terms. **Class terms** holds an offchain class's charter economics from the certificate of incorporation: authorized units, conversion ratio, liquidation preference multiple, participation and its cap, seniority rank, and dividend rate with its accrual basis. It also holds the class's **legal name** as the charter spells it. Display labels derive from the class/series, so renaming is safe, and a rename is the remedy when creating a class is refused as a duplicate. Classes that exist only onchain keep their chain-defined terms and cannot be edited here.

When a class line has activated legal terms (see [Legal classes, series and versioned terms](/using-the-apps/company/captable#legal-classes-series-and-versioned-terms)), the conversion ratio field is locked and labelled "set by the activated legal terms", with a note naming the version, its effective date and its document. To change the ratio, amend and activate a new version in the Tokenization Hub. Otherwise the line's stored terms govern, and the note gives the reason: no legal class attached, no version activated, a conflict between legal identities, or an activated version that records no ratio ("A null ratio is unknown, never 1:1.").

## Token cap table

The **Token cap table** tab tracks project-token claims (SAFTs, SAFTEs, token warrants) apart from company equity, against the token facts you record in **Token config**: name, ticker, network, decimals, total supply, launch date and address, any of which can stay blank until fixed. Fixed claims calculate from those facts. Model-based claims (minimum percentages implied by an instrument's formula) display their recorded terms and show as pending until each formula is implemented from its controlling legal text. Percentages stay unavailable until a total supply is recorded, and the panel says so. The **Percentage denominator** selector switches between **Total supply**, **Recorded allocations** and **Circulating / minted**. **Circulating / minted** stays unavailable until a token or indexer integration is configured.

**Unlock projection date (UTC)** projects each fixed claim's recorded unlock schedule to the start of the chosen day and fills the **Projected unlocked** and **Projected locked** columns. Monthly schedules use calendar anniversaries, clamped to month end. Where a projection cannot be made, the **Projection status** column gives the reason: no unlock schedule recorded, no valid token launch date for a schedule that starts at the token generation event, no verified agreement execution date, or an onchain schedule without verified provenance. The panel states that these are current claims and terms, not a historical ownership record, and that a projected unlocked amount does not establish token availability or permission to transfer.

For a calculated claim, the position form has an optional **Agreement source and calculation inputs** section: the agreement document reference, version and controlling clause, the measurement event and date, an input evidence reference, and the event-specific figures the formula would use (equity ratio, allocable token supply, company reserve, purchase amount, valuation cap, allocated entitlement and allocation references). Leave unknown facts blank. The app does not verify these inputs, and recording them enables no calculation and changes no token total. They travel with the position through the `.csv`, `.xlsx` and OCF exports.


# Token grants and onchain vesting

Equity awards with onchain vesting, covering options, RSUs and restricted stock escrowed through MetaVesT

The **grants** area issues equity awards to service providers (stock options, RSUs and restricted stock) and escrows them onchain in **MetaVesT**, so the chain enforces the vesting schedule. The recipient signs the award agreement in **cyberSign**, and the award appears on the [cap table](/using-the-apps/company/captable) like any other position. Grants run on Base and Ethereum mainnet.

A grant is a cap table position on an underlying stock class (Common or Preferred) whose scrip sits in a vesting escrow. It never becomes a Ledger Entry Token (LET) of its own: the recipient becomes a registered holder only when they convert vested scrip back into a LET. The award type must be recorded. A position on an ordinary class with no award type is a direct holding, and the grant and MetaVesT actions refuse it instead of assuming an RSU.

## The lifecycle

The grants page draws this lifecycle as a diagram:

1. **Reserve** (company): mint shares to the company and scripify them, so there is scrip to escrow.
2. **Grant** (company): record the award on the cap table.
3. **Sign** (grantee): the recipient e-signs the award in cyberSign.
4. **Escrow** (automatic): when they sign, the scrip moves from the company wallet into the vesting contract.
5. **Vest** (grantee): the schedule accrues onchain, and the recipient withdraws vested shares as they unlock.
6. **Exercise** (options only): the recipient pays the strike in USDC to convert vested options into shares.
7. **Settle** (grantee): the recipient converts the scrip back into a LET, a registered holding.

| Award type           | What it is                                                                              | What the grantee does                                          |
| -------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **RSU / vesting**    | Shares vest over time, with no purchase price                                           | Vests, then claims the shares                                  |
| **Stock option**     | A right to buy shares at a strike price                                                 | Vests, pays the strike in USDC to exercise, then claims        |
| **Restricted (RSA)** | Shares escrowed up front, with unvested shares bought back by the company on early exit | Holds from day one, keeps what vested and is paid for the rest |

## Before your first grant

The app prompts for both prerequisites when they are missing.

**Reserve and scripify.** Grants escrow scrip that already exists. **Reserve shares** on the grants page walks you through minting the reserve (for a stock plan, one "Corp--Plan" LET held by the company) and scripifying it. The scripify form is described on the [Tokenization Hub](/using-the-apps/company/tokenization-hub) page, and two of its choices matter for grants:

* A grant that promises irrevocable vested shares cannot escrow into scrip that keeps the issuer's force-transfer, freeze and burn override. Turn on the **No clawback** switch when you scripify a class for such grants.
* The **Who can scripify** whitelist is on by default and starts with the LETs the cap table links as the class's plan reserve, so other holders cannot turn unvested or restricted shares into transferable scrip. A reserve LET minted after the scrip was deployed is not on the list, and scripifying it stops with "This certificate isn't on the class's scripify whitelist" until an officer adds it in the class's **Scripify whitelist** section of the Tokenization Hub.

**An award-agreement template.** **Register award template** uploads your award document, registers it onchain in one transaction, and binds new grants of that award type to it. Each award type needs its own template, because an option carries strike fields an RSU does not. Adopting identical content that is already registered needs no transaction. You can also pin a recipient-specific document to a single grant from its Tokenize dialog.

## Create a grant

**New grant** records the award on the cap table without touching the chain. The form asks for the award type, the recipient and underlying class, the granting plan (drawing down its pool), the units, and the schedule: vesting start and end, a cliff in months (12 by default), and an optional lockup that keeps vested shares in escrow and releases them linearly through a later date.

Paid awards add the strike or repurchase price in USDC per share, the post-termination exercise window (90 days by default for options) or the repurchase waiting period, and the option's term expiration (ten years by default). A preview beside the form restates it as one sentence about what the grantee experiences.

The **issuer override on vested shares** needs a deliberate choice:

* **None (recommended)**: vested shares are irrevocable. Once they vest and are withdrawn, the company has no onchain way to reclaim, freeze or burn them.
* **Issuer keeps force-transfer / freeze / burn**: the company keeps the scrip's admin override, which reaches even vested, withdrawn shares.

Neither choice changes the schedule, and unvested shares forfeit on early termination either way.

The **award agreement** section picks the signing method. **Sign in cyberSign** is the method that supports onchain escrow. You can instead **import** a separately signed document (wet ink, DocuSign) or record **no agreement**. Those grants stay offchain cap table positions that cannot be escrowed, and the form says so when you pick them.

**Create grant** issues the award. **Save as draft** stages it without counting toward anything, to issue later from [Securities status](/using-the-apps/company/captable#securities-status).

## Escrow a grant onchain

**Tokenize** on a grant proposes the onchain deal and produces a cyberSign signing link for the recipient. A **pre-flight checklist** runs first, with a fix-it link for anything that fails:

* you are connected as the corp owner;
* the template is registered and the recipient wallet is linked;
* scrip of the matching class exists, and the company wallet holds enough of it;
* the plan reserve is tokenized;
* the clawback mode is consistent with the scrip's override;
* no termination is recorded and the option term has not expired;
* an advisory, when a transfer-restriction hook will need the escrow (and later the grantee) allowlisted.

Two checks refuse a grant that could never complete. The recipient's selected wallet must differ from the company wallet that controls grants, because an award agreement cannot have the same wallet on both sides. Link a separate personal wallet for the recipient. The company's issuance manager must also be a build with the scrip vault and recertification that let a grantee settle vested scrip into shares. On an older build the checklist says "Upgrade the company's contracts before escrowing grants.", and the grantee's cyberSign page applies the same check and refuses with "The award can't be created". Before anything is sent, the server also re-checks that your signed-in profile is an owner of the corporation.

Then come the transactions:

1. **Set up grants for this corp**, for the first grant only: one transaction deploys the corp's MetaVesT controller.
2. **Sign as grantor**: a free signature.
3. **Approve & propose**: two transactions, an ERC-20 approval of the scrip to the controller and then the deal proposal.

The result is a signing link scoped to the grantee's wallet. The app sends no email, so copy the link and send it to the recipient yourself. Nothing moves until they sign, and the shares escrow when they do.

Onchain vesting follows the market convention. Nothing vests before the cliff, the earned portion vests as a lump at the cliff, and the rest accrues linearly, per second, to the end date. At any moment the recipient can withdraw the lesser of vested and unlocked shares, minus what they already withdrew.

## Manage grants

The grants hub lists every award in two groups: **escrowed onchain**, where the chain enforces the schedule, and **awaiting tokenization**, for awards recorded offchain that you tokenize to escrow. Each row shows the recipient, class, award type, units, vested percentage and a stage track from `GRANTED` through `SIGNING` and `VESTING` to `VESTED`, with per-group subtotals and an all-grants total. Award-shaped positions from an in-app formation sit in a separate **FORMATION-MANAGED** group. They stay cap table entries, and MetaVesT tokenization is not available for them.

Each row has one primary action for its stage, plus a menu:

* **Check status**: after the recipient signs, confirms that the escrow finalized and stamps it on the cap table.
* **Sync cap table**: mirrors onchain events (exercises, withdrawals, buybacks, terminations) into the cap table. It runs once automatically when drift is detected.
* **Backing certificate…**: for a grant funded from a company-held LET instead of a plan reserve (for a founder, typically a LET in the name of the company FBO the founder), links the live LET of exactly the grant's size whose scrip funds the grant. The cap table then counts those shares once, on the grant, and the row notes the nominee holding or warns "⚠ backing cert #N left the company wallet" if the LET moves. **Unlink** reverses it.
* **Copy signing link**, **Open in cyberSign** and **Vesting chart**: navigation and handoff.
* **Void proposal**: cancels a signing link that has not been signed (a free signature plus one transaction). **Re-propose** voids the stale deal and starts a fresh one.
* **Terminate grant**: the unilateral, irreversible stop, confirmed by typing the recipient's name. An option keeps its post-termination window and then forfeits, an RSA stays repurchasable after the waiting period, and an RSU's unvested shares return to the company at once.
* **Recover forfeited** (options, after the window closes) and **Repurchase unvested** (RSAs) sweep the company's side of a termination. Repurchase is two transactions (approve USDC, then buy back), and the payment waits in the award for the recipient to collect.

## The grants authority

Each corp's MetaVesT controller has one **authority**, the wallet that terminates grants, repurchases unvested shares and funds escrows. The **Grants authority** panel on the grants page shows the **Controller** and the **Authority (terminates, repurchases, funds escrows)**, marked "· connected" when the authority is your wallet.

Moving the authority to another corp owner takes one transaction from each side:

1. The current authority picks the new owner under **Hand over to**, checks the address against that owner's own record, and clicks **Start handover**.
2. The nominee connects their wallet on the same panel, sees the handover notice, and clicks **Accept authority**.

The handover is refused while proposals still wait for a grantee's signature, because only the authority that proposed them can void them, so finish or void those first. Only current corp owners can be nominated or accept. The handover moves control only. Unescrowed reserve scrip, the scrip allowance to the controller and company-held reserve LETs stay with the old wallet, so move them to the new authority and approve the controller from it before funding new grants.

Keep the owner who first set up grants on the corp after a handover. The app confirms that the controller belongs to the company by deriving it from that owner, and removing the owner pauses new proposals and reserve minting until it is restored. The officer removal, resignation, ownership transfer and board removal forms show a **GRANTS AUTHORITY** notice, and ask you to tick "I understand, and want to remove this owner anyway.", when the wallet being removed holds the authority or set up the controller.

If the authority sits with a wallet that is no longer a corp owner, the panel shows **GRANTS PAUSED**: "Grants are paused until that wallet hands the authority to a current owner (or is restored as one)." Grants also stop, with an explanation in the panel and in the Tokenize dialog, when the company's grant records point at more than one controller or at one the app cannot confirm belongs to the company.

## For grant recipients

Recipients need no company login. The **my grants** page reads awards for the connected wallet straight from the chain. Connect the wallet the award was issued to, and each grant appears with its status (awaiting your signature, vesting, fully vested, terminated), a vested progress bar, and figures for vested, exercisable, claimable and already-claimed shares.

All actions run from the recipient's own wallet:

* **Review & sign** opens cyberSign. Nothing escrows until you sign.
* **Claim** withdraws vested and unlocked shares, in part or in full.
* **Exercise** (options) pays the strike in USDC for vested options, in two transactions (approve, then exercise). Exercised shares stay in escrow and are claimed as they unlock. The dialog quotes the exact cost from the contract and checks your balance first.
* **Collect** (RSAs) collects, in one transaction, the payment a company buyback left in the award.

Claimed shares arrive in your wallet as scrip. To turn them into a registered holding, convert them back into a LET from My Portfolio (see [For holders](/using-the-apps/holders#convert-scrip-back-into-a-let)).

Each card shows its deadlines: an option's closing exercise window counts down in days, and a terminated RSA shows the date from which the company can repurchase. Grants that are fully vested, claimed and settled move to an archive list. If you also hold positions and LETs, the same view is embedded in [My holdings](/using-the-apps/holders).

> **Under the hood.** Each corp gets its own MetaVesT controller, deployed once from a factory. An award maps to a MetaVesT allocation contract for its type (vesting allocation, token option, restricted token award) that holds [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip) in escrow, denominated in scrip base units at the class's scrip ratio. The award agreement, its template and any bespoke document live in the [`CyberAgreementRegistry`](/building-on-the-protocol/reference/contracts/cyberagreementregistry), the registry cyberSign uses. Termination and recovery are controller calls, and exercise and claim are calls on the allocation.

## Good to know

* **Recording is free, and moving scrip costs gas.** Recording a grant and issuing a draft are cap table writes, and the grantor's and grantee's agreement signatures are free. Escrowing, claiming, exercising, terminating and repurchasing are transactions.
* **The app and the agreement enforce the option term.** The app stops exercise and tokenization after the expiration date you set, and the award agreement governs, but MetaVesT does not enforce the term onchain. The post-termination window does go onchain.
* **Imported and no-agreement grants stay offchain.** They count fully on the cap table but cannot be escrowed.
* **Treat a signing link as private.** Send it only to the recipient.


# The boardRoom

Officers, directors, governance documents and board consents for a cyberCORP

The **boardRoom** is the cyberCORPs app's corporate-authority area: officers and directors, governance documents, board approvals and the company's payment destination. It opens from the app sidebar for a signed-in profile that has one of the company's owner wallets linked (signing in is the free Authenticate signature). Actions that change the company still need an owner wallet connected on the company's network. The company's formation record is in the [Incorporation Hub](/using-the-apps/company#incorporation-hub).

## Corporate authority

### Add and remove officers

The officers table lists everyone holding officer authority: name, address, title and contact. **+ add officer** is one transaction. Each row's action depends on who you are: **resign** on your own entry (or an entry whose wallet is linked to your account), **remove** on anyone else's. Adding an officer can fill in their name and contact from their MetaLeX profile, using only fields they have made public.

The app refuses to remove the last officer or let them resign, because the last officer is the company's last owner and the company would be left with no one able to manage it, permanently. Add a successor first. On a v5 company, the contract revokes a resigning officer's authority only when the wallet has one roster entry and the standard officer role. Before it sends a resignation, the app checks both, and that no role adapter is set for the officer, owner, admin or privileged role, which could keep authorizing the wallet. If a check fails, it explains why and sends nothing. If an owner changes your role or an adapter while the transaction is pending, you can keep authority. After the transaction, the app reads your role and those adapters again and tells you if you still hold a role or adapter authority. It does not check other roles.

Removing an owner whose wallet holds the company's [grants authority](/using-the-apps/company/grants) does not move that authority. Removal, resignation, a remove-and-re-add retitle and the cyberCORP transfer all show a **GRANTS AUTHORITY** warning in that case (and when the app cannot confirm who holds it), and you must tick **I understand, and want to remove this owner anyway** to continue. Hand the authority over on the grants page first if you can.

For a company formed through the app, the officers you named during formation appear here waiting to be published. A panel lists the people "waiting to be added onchain", and **Publish officer to roster** writes each one (name, title and wallet) to the public onchain record, one transaction per officer. The boardRoom is the only place officers are published. The Incorporation Hub shows the roster read-only.

### Seat the board of directors

Directors are officers whose title contains "Director" or "Chair", and the app badges the two separately. The roster comes from titles, with no separate onchain director role, so a director added here holds the same onchain authority as any officer. Add only people you intend to trust with it.

**+ add a director** either promotes an existing officer or adds a new one:

* **Promote an officer** re-titles them ("CEO" becomes "CEO, Director"). The app rejects "&" in titles, so use commas or "and". Promoting another officer takes two transactions, a remove followed by a re-add. If the first mines and the second fails, the app tells you the person is temporarily off the roster and offers a one-click retry. You can't re-title yourself this way, so another officer must do it.
* **Add a different wallet** adds a new officer with a director title.

**Promote an officer** and **keep as officer** (below) run only on contract implementations MetaLeX has reviewed. If the app cannot verify the company's implementation, it says so and sends nothing. **Add a different wallet** is not subject to that check.

**Remove from board** offers two outcomes. **keep as officer** strips the board words from the title ("CEO, Director" back to "CEO"), and **remove from company** removes the entry entirely. A director can also **resign** from the board, which removes their officer entry.

If someone you named a director during formation was published with a title that doesn't convey board capacity, the section flags it and points you to **add a director** to re-title them.

The roster decides who signs [board consents](#board-approvals). With no director-titled officers, consents route to all officers standing in for the board.

### Transfer the cyberCORP

**Transfer cyberCORP** is a two-step hand-over. Add the new owner, who gets full officer control immediately, then either remove yourself to complete the transfer or close the dialog to stay on as co-owners. Removing yourself is blocked until the new owner is confirmed on the roster, so a failed add can't leave the company without an owner.

### Change the payment destination

Investment and agreement proceeds go directly to the company payable address set at incorporation. **Change address** updates it in one transaction, behind a two-step confirmation that shows both addresses and warns that funds sent to the wrong one cannot be recovered. An owner wallet signs the change. If the connected wallet is not one, the dialog names the owner wallets to switch to. Use a company-controlled wallet or multisig as the destination.

## Governance documents

The governance documents archive, backed by the agreement registry, holds the company's constitutive documents, stock plans, consents and anything executed in cyberSign. It is also where you put new documents onchain.

* **Upload PDF**: upload a PDF of up to 75 MB, give it a title and classify it from a taxonomy of about fifty document kinds in three groups: *constitutional* (certificate of incorporation, bylaws, operating agreement, certificate of designation and others), *stock plan*, and *other* (shareholders' agreements, voting agreements, meeting minutes, registers, good-standing certificates and more). A free-text custom type covers anything else. Then list the signers in order (you sign first, and must be a current officer of record) and review. One free signature plus one transaction creates the agreement in the registry with a 30-day signing window. The PDF goes to **public IPFS**, and you send the remaining signers their cyberSign links yourself. Creating documents is enabled only on networks where the registry deployment has been code-reviewed. The archive and linking work on every network.
* **Attach existing agreement** links an agreement that already exists in the registry to this company's archive. An agreement that does not carry this company's signed corp context needs a current officer who is a party to it to attest that it is one of the company's governance records.
* **archive link** and **reactivate link**, on each row, curate the app's association with the document. They change only the app database and never touch the registry agreement.

Each row shows the document type; how the document is bound to the company (*Factory-linked*, *Signed corp context*, *Linked by officer* or *Legacy workflow*); where it came from (*Formation discovery*, *App association* or *Board approvals*); a live status (signed, awaiting signatures, voided or expired); and links to open the PDF or the agreement in cyberSign. Board consents appear in this list too, and have their own workflow under [Board approvals](#board-approvals).

If creating a document is interrupted, the dialog tells you what it knows and does not let you submit twice:

* **BoardRoom link needs recovery** means the registry transaction succeeded but the archive link was not saved. Use **Retry BoardRoom link**, or copy the agreement ID and use **Attach existing**, which then offers **Finish recovery** for that exact agreement.
* **Outcome unknown** means your wallet may have sent the transaction but no receipt was confirmed. Do not submit again. **Check chain evidence** looks for it, and if it never landed, the dialog lets you reopen the same prepared agreement after you confirm that.

## Board approvals

Board approvals put written consents of the board (in lieu of a meeting, in the §141(f) shape) onchain, and the cap table's issuance flow can gate on them: before a position covered by a consent is issued or tokenized, the app checks the consent's status.

**Create issuance consent** (shown when the company has eligible cap table positions and the network's registry supports it) is a three-step wizard:

1. **Positions to cover**: pick the cap table positions the consent approves. Covering a position that is already issued ratifies it.
2. **Signers**: the roster is fixed. Every director signs, or every officer when the board has no directors, because a written consent in lieu of a meeting must be unanimous.
3. **Preview and create**: review the generated PDF, then one free signature plus one transaction creates the consent in the agreement registry with your signature recorded. The dialog warns that the document, including stakeholder names and unit counts, goes to public IPFS, as every signed agreement document does.

You then send each remaining signer their cyberSign link yourself, because the app sends no email. The signing window is 60 days, and a consent is approved when every signer has signed. The app reads a consent's status live from the chain and never stores it. The archive shows per-consent signature counts, whether the board or the officers standing in signed it, and a saved-workflows list for resuming an interrupted consent.

**Record external consent** records a consent executed outside cyberSign. It first asks what the consent approves:

* **General board action** archives the fully executed PDF without linking it to any cap table position. This document-only record does not authorize an issuance.
* **Securities issuance** links the PDF to the uncovered positions it covers, and the issuance gate treats them as approved on your representation.

Both upload the PDF to **public IPFS** behind an explicit acknowledgment. No transaction is involved and nothing is verified onchain. The archive badges these records as external.

**Upload signed approval** serves the formation flow. Reached from the cap table's Securities status queue for one formation-managed draft, it stores the approval PDF **privately**, off public IPFS, and links it to that draft. A **Review formation approvals** button appears here when formation drafts are waiting on one.

Every other consent route publishes its PDF, with names and unit counts, to public IPFS. If that disclosure is unacceptable for an issuance, keep the approval in your minute book instead of recording it in the app. The issuance flow lets you issue a position with no linked consent as an explicit choice.

## Agreement templates

A status board for the company's registered award-agreement templates (one per award kind: RSU, option and restricted stock), with a link to the registration flow in [grants](/using-the-apps/company/grants).

## Board multisig

A preview panel for a BORG board multisig: signing threshold, treasury Safe and pending board actions. A cyberCORP has no onchain board multisig of its own, so the panel shows placeholders until one is deployed for your company.

> **Under the hood.** Officer and director changes are `addOfficer` and `removeOfficer` calls on your [`CyberCorp`](/building-on-the-protocol/reference/contracts/cybercorp) contract, and officer authority is a role in [BorgAuth](/building-on-the-protocol/reference/access-control). The app does not send CyberCorp's in-place `updateOfficer`. v5 has it, but a v5 removal keeps any role other than the standard officer role, so a pending update could change a different officer's entry. A re-title is a remove and a re-add. CyberCorp has no separate onchain director tier, so the boardRoom reads its director roster from officer titles. Consents are agreements in the [`CyberAgreementRegistry`](/building-on-the-protocol/reference/contracts/cyberagreementregistry), created and signed in one transaction.

## Good to know

* **Titles carry meaning.** The board roster and consent routing key off the words "Director" and "Chair" in officer titles, so title people deliberately.
* **Most changes here are transactions.** The exceptions are the external consent uploads and archiving or reactivating a document link, which change only the app database. Message signatures are free.


# cyberSign

Propose, review, sign and countersign agreements in MetaLeX's signing app

**cyberSign** is MetaLeX's signing app, at `app.metalex.tech/cybersign`. Anyone with a wallet can propose an agreement around a PDF and collect signatures from the other parties, and the [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry) records each signature onchain. The cyberCORPs app opens cyberSign from its **cyberSign** sidebar item (see [Run your company](/using-the-apps/company)), and each board consent or governance document created in the [boardRoom](/using-the-apps/company/boardroom) gives its remaining signers a cyberSign link.

cyberSign has three pages:

| Page                 | Address                                                | What you do there                                                        |
| -------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------ |
| **New agreement**    | `app.metalex.tech/cybersign/create`                    | Propose an agreement. Opening `/cybersign` lands here.                   |
| **Agreement page**   | `app.metalex.tech/cybersign/<chain id>/<agreement id>` | Review the document and its terms, and sign. This is the link you share. |
| **Contract Library** | `app.metalex.tech/cybersign/library`                   | Find the agreements and documents tied to your wallet.                   |

## Sign an agreement you were sent

Open the link. The agreement's onchain terms sit on the left under **Agreement Details**, and the document on the right, with a signing bar beneath it. With no wallet connected, the bar offers **Connect Wallet**.

### Review the terms

The left pane shows, from the top:

* **Network**, the chain the agreement lives on. You sign on that network, and your wallet is asked to switch to it if needed.
* **Agreement expiry**, the deadline for signatures, as a date with an **ends in** countdown, or **No expiry** when there is no deadline. The row disappears once the agreement is complete or voided.
* **Contract Details**, the agreement's global fields: name and value pairs the proposer chose to record onchain, such as key business terms.
* **Party #1 Details**, **Party #2 Details** and so on. Each gives the party's wallet address in full, so you can check every character, the **MetaLeX profile** behind that wallet when it has one, and the party's own fields, by default a name and a contract role.

The document opens in your browser's built-in PDF viewer. If the proposer password-protected the PDF, the viewer asks for the password. The proposer gives it to you outside cyberSign, and MetaLeX never has it.

### Approve a free signature, then a transaction

1. In the signing bar each party has a signature block labeled with its address, and unsigned blocks read **click to sign**. Yours is highlighted. Click it. Your wallet shows the agreement data you are consenting to (see [What your signature contains](#what-your-signature-contains)) and asks for a signature, which costs nothing.
2. **Sign Agreement** becomes active. Click it and confirm the transaction in your wallet. It records your signature in the registry and costs gas on the agreement's network.

If the wallet hasn't accepted the current MetaLeX Terms of Service, a **Terms of Service** dialog appears before the transaction. **I Accept** records the acceptance for that wallet (a wallet that isn't signed in is first asked for a free sign-in signature), and the dialog comes back only when the Terms change.

When the transaction confirms, an **Agreement signed** notice appears and your block reads **cyberSigned by** with your name. Your signature covers the exact field values, so editing one of your fields after step 1 discards it and you sign again.

### Take an open slot

A proposer can leave a party's address blank. That party is an open slot, and any wallet not already named in the agreement can take it by signing. When you take a slot, its fields are yours to fill in. **Name** starts from your public MetaLeX profile name, with a note that you can edit or clear it before signing because it will be recorded onchain. The values you enter are part of what you sign.

A named party's fields were set by the proposer and can't be edited; you sign them as they stand.

### Deadlines, voided and complete agreements

Once the deadline passes, the registry refuses new signatures, and any one party can void the unfinished agreement (see [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement)). An agreement with **No expiry** never lapses this way.

The agreement page doesn't label a voided agreement. If you're unsure, check its status in your [Contract Library](#find-agreements-in-your-contract-library). The registry also refuses a signature on a voided or complete agreement, so such a transaction fails and records nothing.

An agreement proposed in cyberSign completes with its last signature: every block reads **cyberSigned by** and the **Sign Agreement** button goes away.

## Propose an agreement

Open cyberSign and sign in with **Connect Wallet** at the top right. Signing in fills Party 1 with your wallet, and uploading a PDF requires it. The form is titled **Agreement Details**; once it has a document, a preview of the PDF and the signing bar appear beside it.

### Set the network, title, document and deadline

* **Network** sets where the agreement lives: Ethereum (the default), Arbitrum or Base. Every party signs there and pays its own gas.
* **Title of Legal Contract** (for example "Stock Purchase Agreement") is recorded onchain.
* **Paste a link to your document, or upload it** takes a link to a PDF or an upload. An upload goes to public IPFS and fills in the link.
  * A PDF that already has a password shows **Document password protected** and uploads as is.
  * Otherwise the form shows **Document not password protected** and offers a **New Password** with **Encrypt with AES-256 and upload**, which encrypts the PDF in your browser before uploading it, or **Upload without password**, which publishes the readable PDF. You send the password to the other parties yourself.
* **Latest Date/Time Agreement Can Be Signed** is the deadline, in your local time. It defaults to 30 days out and can't be left empty.

### Add parties and fields

Under **Parties to the Legal Contract**, each party has a **blockchain address** (the wallet that will sign) and optional **Name** and **Contract Role** fields. An address with a MetaLeX profile shows its profile chip, so you can confirm who it belongs to. A blank address makes the party an **Open slot** that any wallet can claim by signing. **+ add party**, **Remove Party**, **+ add additional fields relating to this Party** and **Remove field** shape the list. Party 1 is the proposer and needs a real address.

The form warns that everything entered here is recorded onchain. For privacy, give a party's address alone, or a pseudonym as its name.

**Global Fields** (optional) are name and value pairs about the whole agreement that every signer attests to: business terms you want readable without opening the PDF, or values the document text refers to.

### Create the agreement

Once the title, document and deadline are set, click **click to sign** on your block in the signing bar and sign the free message. The button then reads **Create Agreement**. Click it and confirm the transaction, which creates the agreement and records your signature in one step. An **Agreement proposed** notice appears and the app opens the new agreement page.

Copy that page's address and send it to the other parties yourself. A one-party agreement is complete as soon as it is created.

Changing anything in the form after you sign the message discards the signature. The draft survives moving between cyberSign pages in the same tab (to **← My Contract Library** and back, say). It is never written to browser storage and is cleared when you reload, sign out or switch accounts.

If Party 1's wallet has delegated agreement signing to your wallet (see [Delegate signing for a Safe](/using-the-apps/profile#delegate-signing-for-a-safe)), you can enter that address as Party 1 and sign for it; the registry records Party 1 as the signer.

## Find agreements in your Contract Library

The **Contract Library** (`/cybersign/library`, or **← My Contract Library** on the new-agreement form) lists the agreements and documents tied to the connected wallet, newest first, with **New agreement** to start another. Each row has a source tag, the parties, a date and a status, plus **PDF** (the document) and **Open** (the agreement page) where they apply.

| Source tag                                   | What it lists                                                                                                                                                         | Statuses                                         |
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| **cyberSign**                                | Registry agreements that name your wallet as a party, on Ethereum, Arbitrum, Base and zkSync Era                                                                      | **Pending**, **Signed**, **Expired**, **Voided** |
| **Tokenization Hub**                         | Ledger Entry Tokens (LETs) your wallet holds. **Open** goes to the agreement the LET was issued under, when it has one.                                               | **Recorded**, **Voided**                         |
| **cyberRaise**, **Formation**, **Cap Table** | Files the cyberCORPs and cyberRAISE apps store (pitch decks, formation documents, cap-table grant documents) for companies where your wallet is a founder or investor | **Signature not verified**                       |

**Recorded** and **Signature not verified** describe what the library can prove: a LET record or a stored file is no evidence that anyone signed. Private cap-table grant documents appear only after you **Sign in** from the banner at the top. Source chips filter the list when more than one source has entries, and the search box matches titles, document types, company names and party names.

## What your signature contains

Every signature in cyberSign is an EIP-712 typed-data message addressed to the registry: domain `CyberAgreementRegistry`, version `1`, on the agreement's chain and registry address. The message (`SignatureData`) contains:

* the agreement id (`contractId`);
* the party you are signing for (`signer`), on registries that include it;
* the document link (`legalContractUri`);
* the global field names and values;
* the party field names and your values.

Before your wallet prompts, cyberSign reads the registry's signature format and builds the matching message. The registry on Ethereum, Base and Arbitrum includes `signer`, and the zkSync Era registry uses the older format without it. cyberSign refuses a registry whose format it doesn't recognize. Before sending the transaction it checks that the network, registry and format still match your signature, and asks you to sign again if they don't.

> **Under the hood.** Proposing calls `createStandaloneContractAndSignFor`, which creates the template (if new) and the agreement, with no finalizer, and records your signature in one transaction. Countersigning calls `signContract`. On Ethereum, Base and Arbitrum the registry is the proxy `0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134`. Templates, open slots, voiding and delegation at the contract level are covered in [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement) and the [CyberAgreementRegistry reference](/building-on-the-protocol/reference/contracts/cyberagreementregistry).

## Good to know

* **Agreements are public unless you encrypt the PDF.** An uploaded PDF sits on public IPFS, and the title, deadline, party addresses and every field value are onchain for anyone to read. Password protection keeps the document text private; the onchain fields stay public.
* **Which steps cost gas.** Clicking a signature block is a free message signature. **Create Agreement** and **Sign Agreement** are transactions.
* **You deliver the links.** cyberSign doesn't email anyone. Parties who turned on [Telegram notifications](/using-the-apps/profile#get-notifications-on-telegram) get a message when an agreement naming them is created, signed, fully signed or voided, with an **Open agreement** link.
* **There is no void button.** A party requests a void from the registry directly (see [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement)).
* **A delegate can propose but not countersign.** A delegate wallet can create an agreement for the party that delegated to it, but the agreement page can't submit a delegate's signature on an existing agreement: the transaction is rejected. Countersign from the party's own wallet.


# cyberRAISE

Run a raise as an issuer or invest in one, from the first draft to the issued security

**cyberRAISE** (`cyberraise.metalex.tech`) is the fundraising app: companies create and run their rounds here, and investors invest. The cyberCORPs app's **cyberRaise** sidebar item opens it.

![cyberRAISE](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-6acb965a7914aa7cc3a9cf67c96f146be21f4fd3%2Fcyberraise-home.png?alt=media)

## Run a raise

A new raise first asks **how would you like to structure your raise?** Without a cyberCORP, the flow creates one first, with the same Network, Legal Identity and Public Profile steps as the cyberCORPs app, and deploys the company and the round together at the end. A raise needs a legally formed company; the cyberCORPs app can [form an LLC or C-Corp](/using-the-apps/formation) for you.

A setup you resume, or open from a saved draft link, keeps the company it was started for. If a saved draft lacks the company, the round or the template, the form shows **Complete your raise setup**, and **Continue setup** returns you to that step with your other answers kept.

### Plan the financing first (optional)

Above the two structure choices, **Want help choosing your financing?** links to **Open my financing plans**. A financing plan (*Your financing plan*) is a guided questionnaire in four parts (*Issuer and financing direction*, *Existing commitments and token plans*, *Investors and deal process*, *Terms and preparation*) that ends with a preliminary financing direction and the items still open **Before detailed setup** and **Before execution**. GAIBE, MetaLeX's AI agent, can walk you through each part (**Discuss with GAIBE**); a change it proposes to your answers applies only when you accept it. Plans are private to your account, need you signed in, and resume on any device. **Save and continue my financing plan** in the entity guide of the formation flow carries its relevant answers over for you to confirm.

**Save and prepare a fresh financing draft** opens a new raise setup, where you choose the company (*Which company will use this financing plan?*) and confirm it is the issuer the plan describes. **Use reviewed round structure** copies one answer, ticket by ticket or structured round, into the setup; you set everything else in the normal steps. The plan is guidance and gives no legal clearance.

### Choose ticket by ticket or a structured round

* **Ticket by Ticket** sells securities to investors one at a time, each on its own terms. There is no shared target and no minimum or maximum; each ticket is an individually configured deal. It suits privately advertised raises and geographically restricted (Regulation S) raises.
* **Structured Round** runs an automated round on the same terms for every investor, with a target raise, minimum and maximum ticket sizes, and escrowed investor bids. It can be public or private.

![The raise-structure choice](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-002439b8ea65e5b43635df1011cd1ac6bd3252b5%2Fstart-raise.png?alt=media)

### Configure a structured round

The wizard has three steps. Progress saves as you go, and the header icons **save your progress** locally or **save to the cloud** for a shareable link.

**Step 1, initialize the round.** The form asks, in order, for:

* **Round stage**, the security series (e.g. Pre-Seed), which increments from your last round.
* **Round type**:
  * **Privately Advertised - U.S.** keeps the round invite-only.
  * **Publicly Advertised - U.S.** lists the round on the public-rounds Marketplace, and every investor must be accredited through [LeXcheX](/using-the-apps/lexchex).
  * **Publicly or Privately Advertised - U.S. Excluded** is a Regulation S round. Participants prove they are not U.S. persons with a passport scan in the zkPassport mobile app, unless you approve one manually as an exception (see [zkPassport overrides](/using-the-apps/ace#zkpassport-overrides), which also explains how far an override reaches). This type is offered on Ethereum and Base and is marked unsupported on Arbitrum.
* **Admission mode**:
  * **First-Come, First-Served** accepts offers automatically, in order, and escrows the funds at once until the round fills.
  * **Investors Bid / Founders Approve** lets you review each bidder (profile, socials, reputation) and choose who gets in and for how much.
* **Ticket size**, the minimum and maximum one investor can invest.
* **Funding target/cap**, a hard cap. The round ends when it is reached.
* **Start and end dates**, or **Open ended** for no end date. An open-ended founder-approval round makes every offer exploding.
* **Exploding offers** (founder-approval rounds only), which lets investors send time-limited offers.
* **Closing conditions**. **Allow early close** lets you close the round before its end date or once it is fully funded, and investors see a "may close early" note on the round.
* **Pitch deck**, an optional description and up to three files.

**Step 2, choose the ticket type.** Pick the deal paper: SAFE, SAFT, SAFTE, or SAFE + Token Warrant, each in a Reg D and a Reg S variant. Choose the variant by who invests in the round:

* If any investor in the round is a U.S. person, use the Reg D variant for the whole round, including the non-U.S. investors. Non-U.S. investors in a Reg D round must also be accredited investors.
* Use the Reg S variant only if no investor in the round is a U.S. person.
* Do not split one round into Reg D tickets and Reg S tickets. In a ticket-by-ticket raise you choose the type for each ticket, so apply the rule to every ticket in the round.

"U.S. person" has its Regulation S meaning ([Rule 902(k)](https://www.law.cornell.edu/cfr/text/17/230.902)). It includes anyone who lives in the United States and any corporation or partnership organized under U.S. law. For an individual the test is residence, so a U.S. citizen who lives abroad is generally not a U.S. person.

Investor status is not the only Regulation S condition. Each sale must also be an offshore transaction ([Rule 902(h)](https://www.law.cornell.edu/cfr/text/17/230.902)):

* The offer is not made to a person in the United States.
* When the investor places the order, the investor is outside the United States, or the company and anyone acting for it reasonably believe so.

No directed selling efforts may be made in the United States by the company, a distributor (for example a placement agent), an affiliate of either, or anyone acting for any of them ([Rule 903](https://www.law.cornell.edu/cfr/text/17/230.903)). Some offerings have more Regulation S conditions.

The round type from step 1 can settle the variant for you:

| Round type                                       | Ticket types offered                                                                      |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| Publicly Advertised - U.S.                       | Reg D only. The round relies on Rule 506(c), and non-U.S. investors can invest in it too. |
| Publicly or Privately Advertised - U.S. Excluded | Reg S only.                                                                               |
| Privately Advertised - U.S.                      | Reg D and Reg S. Apply the rule above.                                                    |

Custom templates approved in the MetaLeX console appear as extra cards here and in ticket-by-ticket raises, and the round type filters them the same way. Each card says whether the template was registered for your company or approved by MetaLeX for any company. The *Custom* option takes a template name or registry ID agreed with MetaLeX, which the app checks against the onchain registry. If the series already has a line, you add a **sub-series label** (e.g. "2" to run Series A-2) so the new round's onchain identifiers stay unique. If the company already has a LET contract for the series and deal type, an **Existing cert printer** field shows it and the round reuses it.

**Step 3, set up the agreement.** Configure the standard agreement investors will sign. Submitting opens a **round summary** (network, round type, admission mode, ticket size, funding cap, valuation, dates, dispute resolution), and **Confirm & Submit** deploys the round onchain.

Signing the round agreement is a free signature. When the company is created in the same flow, your wallet asks for a second free signature that approves the company's deployment details, which the agreement signature does not cover. For an existing company, the app reads which round manager the company uses, and that contract's version, before you sign and again before it sends the transaction. If the wallet, the round or the contract changed in between, it stops with "The wallet, round or contract changed. Review and sign again." It refuses to send to a round manager that reports a version the app does not support.

Filling in the forms is free. MetaLeX takes **0.3% of the funds the issuer claims**; investors pay nothing, and rejected bids are never charged (see [What it costs](/using-the-apps/webapp#what-it-costs)).

> **Under the hood.** A round is created on your cyberCORP's [`RoundManager`](/building-on-the-protocol/reference/contracts/roundmanager). The contract-level walkthrough (building the round, taking EOIs, allocating and closing) is [Run a cyberRAISE round](/building-on-the-protocol/how-to/run-a-cyberraise-round).

### Manage your rounds

The company's **rounds list** shows its active and closed rounds, each with its series, public or private label, structure, progress against target, and a flag when EOIs are waiting for you.

Opening a round shows its **management view**: three summary cards (Round, Offer, Raised) and, for a founder-approval round, tables of **Pending Offers** (EOIs awaiting your decision), **Exploding Offers** (time-limited ones), **Completed Offers** (EOIs you have allocated) and **Issued Certificates** (the LETs the round has issued), with a checkbox that reveals expired and rejected offers. A first-come round needs no review, so it shows completed tickets and issued certificates.

The view also offers **Investor view**, **Edit Pitchdeck** (once you are signed in) and, if you allowed early close, **Close Round**. A round that has raised nothing can be **hidden**, which removes it from public view permanently.

A **ticket-by-ticket** round lists its pending and completed tickets, and **Set up a Ticket** starts a new individually configured deal, with a dropdown when the round has more than one agreement template. Ticket tables name each ticket's template, custom templates included.

The rounds list and the management view link to the company's [public company page](/using-the-apps/company), which anyone can read without a wallet.

### Set up a ticket for an existing company

A ticket for an existing company mints its securities from the company's LET contracts. How the offer gets those contracts depends on the company's version (see [v4 and v5 companies](#v4-and-v5-companies)).

**On a v3 or v4 company**, the app reuses the LET contract that matches each certificate's type, series, legal document and payment token. If none matches, as on a company's first ticket, the offer creates the LET contracts in the same transaction as the offer. The company, its deal and issuance managers and each reused LET contract must all report the same major version, 3 or 4. A v4 company can report "4" while a manager reports "4.1". If one of them reports another major version, the app stops before you sign.

**On a v5 company**, each security in the offer needs a class identity and active class terms before the ticket can be signed:

1. The form opens **Set up the security for this offer**; **Review security class setup** opens it on demand.
2. For each certificate, pick the **Cap table class or series** it belongs to, or create one with **New class name**, **Series name** and **Governing document reference**, then **Save class identity**.
3. Save, approve and activate the class terms. These steps need a current officer wallet of the company, and **Approve class terms with wallet** is a free signature.
4. Link a LET contract. Either choose a compatible existing one and **Link selected printer**, or **Prepare a new printer** and **Deploy reviewed printer**, which is a transaction. If the deployment is interrupted, paste its transaction hash and **Check transaction and finish linking** instead of deploying again.
5. **Return to ticket and sign** takes you back to the ticket with your entries intact.

Saving the class records no position and issues no units. It ties the tokenized and untokenized positions of that class together in the [cap table](/using-the-apps/company/captable).

On any other version the ticket form stays blocked with "This company's contract version is unknown or unsupported." ACE tickets follow the same rules. After you sign a ticket, a change to its template, the company, the network, the class terms, the LET contract or your wallet invalidates the signature, and the app asks you to review and sign again.

### Allocate or reject an EOI

**Review** on a pending offer opens the EOI: the investor's profile and bio, the offer (minimum and maximum amount, their message, any expiry), the agreement details, their trading activity and their LeXcheX accreditation status. You then either:

* **Allocate**, entering an amount within the range for a minimum-maximum offer, and confirm the onchain transaction that accepts them; or
* **Reject**, and confirm the onchain transaction that declines the offer.

An agreement stays with the round manager that finalized it, so allocation goes to that manager even if the company has since switched to another one. Before you confirm, the app checks onchain that the manager still holds the investor's escrow unallocated and runs a version the app supports. If a check fails (the offer is already allocated, the escrow belongs to another investor, the version is unsupported), it tells you before you sign and sends no transaction that would revert.

> **Under the hood.** Allocating an EOI releases the investor's escrowed funds to the company and mints their security as a LET through the [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager). Each party's legal agreement is anchored in the [`CyberAgreementRegistry`](/building-on-the-protocol/reference/contracts/cyberagreementregistry).

### Close a round

**Close Round** is a single transaction. It stops new EOIs at once, and you can still review and allocate the EOIs already submitted. When a round hits its cap, the app prompts you to **initialize the next round**.

### Review a term sheet with GAIBE

**Term sheet** in the sidebar, or **Review a term sheet** on the cyberRAISE home page, opens *Term sheet review*. Upload one financing term sheet as a PDF of up to about 3 MB, and GAIBE reads it and returns a report:

* counts of **Terms identified**, terms that **Need review**, terms **Outside / partial** the cap table model, and **Pages cited**;
* **Core terms**: valuation, liquidation preference, participation, anti-dilution, board composition and protective provisions. Each is marked *Identified*, *Ambiguous* or *Not found*, with what GAIBE extracted, quoted evidence with its page number, and a **MetaLeX fit** label for how far the cap table can record it (*Cap table supported*, *Partially represented* or *Outside cap table model*);
* **Additional material terms** it found, such as the option pool, dividends, pro rata rights or redemption, each with the same fit label.

You can copy the report or download it as Markdown. A review can take a few minutes. Any signed-in user can run one, up to a daily limit per account. The PDF and the report are not stored and are not added to a cyberCORP or a cap table. The page labels the result an AI review that is not legal advice. A term GAIBE reports as absent may still be unfavorable, and GAIBE can miss or misread text, so use the report to focus your review with counsel.

## Invest in a round

### Find a round

**Public Rounds**, the Marketplace, is searchable and split into **Open Rounds** and **Past Rounds**. Only publicly advertised rounds appear there; you reach a private round through a link the issuer gives you.

![The public-rounds marketplace](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-1a2dc0a5180df7d89c84e0ef7dfb6bbc7d995aa4%2Fpublic-rounds.png?alt=media)

A round's detail page shows the company, the security and series, the round's terms and progress, its eligibility badges, and the **Invest** or **Express Interest** button. The terms panel is headed by the round's security class, for example *SAFE Details*, or *Round Details* when the class can't be read. Links to a public round page and its express-interest page carry a preview image, so apps that build link previews show a card.

![A round's public detail page](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-913255f3c4810a9152328158339874df8dbba71f%2Fround-detail.png?alt=media)

If you hold no valid [LeXcheX](/using-the-apps/lexchex) accreditation, the list shows an **"I am investing as:"** card. Toggle between *an individual* and *a legal entity* to see the routes to accreditation: LeXcheX verification, investing above a threshold ($200k+ for an individual, $1M+ for an entity), or manual verification by a MetaLeX attorney. A public U.S. round needs one of them. A Regulation S round instead needs a non-U.S. passport scan through zkPassport.

### Express interest

The *Express Interest* screen (titled *Invest in …* on first-come rounds) puts the form and the legal agreement side by side on a desktop; on a phone you swipe between the two panels. Your first visit opens with a plain-language notice that this is a legally binding investment, issued as a tokenized security and countersigned by the company.

![The invest screen, with the deal notice and the agreement alongside](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-2e184656183fbab8e1d0900db5d65c7fcadfc4e1%2Fexpress-interest.png?alt=media)

You provide:

* **your investor details**: name, contact, investor type and, for anyone other than an individual, jurisdiction of formation. They prefill from your [profile](/using-the-apps/profile) and can be encrypted; the first time you submit, a **privacy settings** dialog asks what to encrypt;
* **the investment amount**: a fixed amount or, in a founder-approval round that allows a range, a minimum and maximum, within the round's ticket limits and your wallet balance;
* in a founder-approval round, an optional **message** to the founder and your own optional **exploding offer** expiry (24, 48 or 72 hours, or a custom date).

If the round requires accreditation, you confirm an existing LeXcheX credential or mint one here. A Regulation S round asks for zkPassport verification instead.

Then:

1. **Sign the agreement**, a free signature.
2. **Approve the payment token** if needed, an onchain transaction.
3. **Submit**, the onchain transaction that places your EOI.

In a founder-approval round, your maximum amount stays in escrow until the founder accepts your offer or the round closes, and any unused remainder comes back to you. In a first-come round, an accepted investment mints the LET at once. If the issuer allowed early close, the round may end before its scheduled date.

Your signature covers the round's terms as they stand when you sign. Before the token approval and again before submitting, the app reads the round's company, round manager and terms onchain and simulates the exact transaction. It stops with a message such as "Round terms changed. Review and sign again." when, since you signed, the terms changed, the company moved to a different round manager, or that manager was upgraded to a new major version (v4 to v5, for example). You then sign the current agreement. An upgrade within the same major version keeps your signature valid. The app refuses to submit to a round whose manager reports a version it does not support.

> **Under the hood.** Your EOI is an EIP-712-signed offer recorded on the [`RoundManager`](/building-on-the-protocol/reference/contracts/roundmanager). Until the round resolves, your funds sit in an onchain escrow that MetaLeX does not hold and no one can override (see [Upgrades and control](/how-cybercorps-works/upgrades-and-control)). The security you receive is a Ledger Entry Token (LET) minted by the class's LET contract; where the company's governing documents make the onchain record its securities ledger, that LET is your ledger entry. See [LETs and scrip](/how-cybercorps-works/lets-and-scrip).

### Track your investments in the portfolio

**My Portfolio** shows your **pending investments** (EOIs awaiting a decision, with their expiry), your **closed investments**, the securities you hold (**Owned Securities**) and any scrip (**Owned Scrips**). Checkboxes reveal voided, expired and rejected entries. If an EOI expires unanswered, **Recall** returns your escrowed funds.

The portfolio is also where you scripify a LET, transfer LETs and scrip, and request re-certification ([For holders](/using-the-apps/holders) walks through each), and where you bridge ACE-round tokens to Solana (see [the Solana bridge](/using-the-apps/ace#the-solana-bridge)).

## v4 and v5 companies

On Ethereum, Base and Arbitrum, a new company is a v5 company, including one created inside a cyberRAISE flow. An existing company keeps the version it was deployed with until its owners upgrade it from the cyberCORPs app (see [Run your company](/using-the-apps/company)). cyberRAISE supports both. It reads the version of the specific company and contract it is about to use, never assumes one for the whole chain, and refuses an unknown version, or one newer than it supports, with a visible message.

* **Structured rounds** are set up, signed, funded and allocated the same way on v4 and v5 companies.
* **Tickets for an existing company** differ, as described in [Set up a ticket for an existing company](#set-up-a-ticket-for-an-existing-company). Some older companies run v3. For tickets, the app treats a v3 company the same way as a v4 company. The app does not support tickets for v1 or v2 companies.
* **A signature is tied to the major version** of the manager it was given for, whether the founder's or an investor's. If the company upgrades that manager from v4 to v5 before the transaction is sent, the app asks for a fresh review and signature and never sends the old one.

The *Contracts and deployed versions* section of a company's [public company page](/using-the-apps/company) reads each contract's version live and says whether it matches the reference its factory publishes.

## The MetaLeX console (staff)

MetaLeX staff have an admin console at `/console` on any of the app subdomains, open only to an allowlist of admin wallets. The parts that touch cyberRAISE:

* **Raises** is a read-only explorer of every raise on the platform, structured rounds and ticket-by-ticket deals, across every cyberCORP and chain. It has search, filters and sortable columns (cyberCORP, round, type, chain, created, participations, progress, status). A raise opens to its full terms, documents and participations, including each participation's **signed legal document** with its parties and signature status.
* **cyberCERTs** is a read-only list of every LET issued on the platform, grouped by company.
* **Templates** is where staff register and approve custom templates, for one company or for all, which then appear in the ticket-type step of structured rounds and ticket-by-ticket raises.
* **Curation** is where staff blacklist cyberCORPs and hide spam rounds from the public Marketplace.


# ACE

Raise from a token community with a SAFE priced in its token, inside cyberRAISE

**ACE** is MetaLeX's fundraising product for token communities. An ACE raise sells a **tokenized SAFE** priced and paid in the project's community token, so members of the community become equity holders of the company behind the project.

ACE runs inside [cyberRAISE](/using-the-apps/cyberraise) (`cyberraise.metalex.tech`), and links to `ace.metalex.tech` redirect there. ACE rounds run on **Base**.

## What sets an ACE raise apart

* The raise is **denominated in a community token** instead of USDC. The token can be a Base **ERC-20** (the default) or a **Solana token**, in which case funding involves bridging it between Solana and Base.
* The deal paper is an **ACE SAFE**, in a Reg D and a Reg S variant.

Round types, admission modes and escrow work as in any other cyberRAISE round.

## Set up an ACE raise

You create an ACE raise through the normal cyberRAISE flow: create your cyberCORP if you don't have one, initialize the round (dates, ticket size, funding cap, round type, admission mode), and pick **ACE SAFE (Reg D)** or **ACE SAFE (Reg S)** as the ticket type. The final **SAFE Setup** step is specific to ACE:

1. **Identify your token.** Choose the **token type**, ERC-20 or Solana token (Solana bridging is supported only on Base), and paste the token address or Solana mint address. The token's name, ticker and image load automatically.
2. **Set the terms.** Enter the **entity valuation** as a token amount and the **current token percentage**, choose a dispute-resolution method, and add any optional **custom provisions**. The raise cap, minimum and maximum investment, company identity and payable address carry over read-only from the earlier steps.
3. **Sign.** Signing the agreement preview is a free signature. A raise that also creates the company then asks for a second free signature, **Sign to approve round**, which approves the company's deployment details and the token. ERC-20 and Solana-token raises both ask for it.
4. **Review the summary.** The *New ACE Round Summary* shows the network, round type, admission mode, ticket size, funding cap, valuation, dates and dispute resolution, and **Confirm & Submit** deploys the round onchain.

Drafts save automatically as you fill in the form, and the header icons save the draft to the cloud and share a resumable link. A **support chat link** ("Need help?") stays on the form throughout.

A live ACE round's header has the usual management actions plus **Bridge \<token> to Solana** and **Set up a Ticket**, which starts an individually negotiated ticket (Reg D or Reg S). It also has **Manage zkPassport Overrides**, which appears on any round gated to non-U.S. persons, ACE or not.

ACE tickets use the same ticket form as other cyberRAISE tickets. A new ACE raise creates a v5 company, where the ticket form first has you choose each security's class and set up its terms and LET contract. On a v3 or v4 company the offer reuses or creates its LET contracts itself. [Set up a ticket for an existing company](/using-the-apps/cyberraise#set-up-a-ticket-for-an-existing-company) gives the steps and what happens on other versions.

### zkPassport overrides

On a round restricted to non-U.S. persons, the founder can manually approve a verified investor as an exception. This works on any round that carries the non-U.S. condition, including a USDC-denominated Regulation S round.

To find it, open the round from your rounds list so you are on its **management view** (the founder console with the Round, Offer and Raised summary cards, as opposed to the public investor page), with your owner wallet connected. The action bar at the top, the row with **Investor view** and **Edit Pitchdeck**, shows **Manage zkPassport Overrides**. On an ACE round it sits between **Bridge \<token> to Solana** and **Set up a Ticket**.

![The round management view, with Manage zkPassport Overrides in the action bar](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-b027aadfb2eaf7cb2414649b68dfa97a33017983%2Fzkpassport-overrides-button.png?alt=media)

The button is hidden unless the round carries the non-U.S. condition and the connected wallet owns the company. It is also hidden on ACE rounds that use the first version of the non-U.S. condition contract, which has no override function.

On the overrides page, enter the **investor wallet** and **Add override**. Adding and removing an override are both onchain transactions. The page lists active overrides with when and by whom each was added.

![The zkPassport Overrides page](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-25227334d41d94baa5f46ccb8e073460f61e6564%2Fzkpassport-overrides-page.png?alt=media)

An override is granted against your company's round manager, so it covers more than the round you opened it from. It satisfies the non-U.S. check on **every** round of yours that shares the condition. Grant one only to an investor you would except from all of them.

## Invest in an ACE round

1. **Open the round.** The detail page shows the company, the security, the status (open, funded or closed), an elevator pitch, the founder's profile and any resource documents. A "Help with sharing your signal?" prompt offers **Stay Anon** or **Amplify**; opting in lists you among the round's key investors.
2. **Verify eligibility.** A round restricted to non-U.S. persons asks for a **zkPassport** verification: a passport check in the zkPassport mobile app that proves you are not a U.S. national and not from a sanctioned country without revealing your identity. Submitting the proof is an onchain transaction, so you need a little ETH on the round's network (typically under $1).
3. **Invest.** The invest flow opens with a notice that this is a legally binding investment issued as a tokenized SAFE. You give your name, contact, investor type (and jurisdiction of formation for an entity) and the amount. In a first-come round your investment is accepted and the LET mints at once; in a founder-approval round your funds wait in escrow until the founder decides.
4. **Bridge if needed.** A Solana-token ACE round is funded in a Base-wrapped version of the token. On these rounds, clicking connect first shows a short preface: you connect a wallet on the round's EVM chain to continue, and you connect your **Solana wallet** separately inside the investment form. If your Base balance is short, an in-page bridge card walks you through bridging (see [the Solana bridge](#the-solana-bridge)).
5. **Track your holdings.** Your ACE SAFEs appear in the cyberRAISE **Portfolio** with your other investments.

## The Solana bridge

A Solana-token ACE raise runs on Base, so the community token crosses between the chains over the **Base ↔ Solana native bridge**. Separate *Bridge to Solana* and *Bridge from Solana* pages handle each direction (a header toggle switches between them), and the invest form embeds the Solana-to-Base flow for investors whose Base balance is short.

You need both wallets connected, Solana and EVM, and about 0.01 SOL for account rent and relay fees on top of the token you are bridging. Each bridge action asks you to accept the Terms of Service before signing.

**Solana → Base** takes one signature. A single Solana transaction sends the token into the bridge and pays for relay, and the page then watches Base until the wrapped token reaches your wallet. It allows up to ten minutes, and the transfer usually takes much less.

**Base → Solana** takes three actions spread over the bridge's validation cycle:

1. **Bridge.** A Base transaction locks the wrapped token for your Solana address. The page writes the transaction hash into its URL, so you can close the tab and resume later, and a "past transfers" list reopens any transfer still in flight.
2. **Prove.** After validators post the checkpoint to Solana (about 20 minutes), a Solana transaction proves your message.
3. **Claim.** A final Solana transaction releases the token and creates your token account if needed.

Neither page charges a protocol fee. You pay Base gas, Solana fees and rent.

## Under the hood

ACE uses the protocol's **ACE / PumpCorp** path. An ACE raise deploys a cyberCORP configured for a token-to-equity offering and issues **ACE SAFE** Ledger Entry Tokens (LETs), a SAFE variant whose security series is `ACE`. The non-U.S. eligibility check is an onchain **condition**, the zkPassport-backed `NonUSNationalityCondition`, and a founder override writes to that condition contract.

* The offering type at the protocol level: [Deploy a PumpCorp for ACE](/building-on-the-protocol/how-to/deploy-pumpcorp-ace) and [Factories](/building-on-the-protocol/reference/factories).
* How eligibility gating works: [Conditions](/building-on-the-protocol/reference/conditions) and [Compliance architecture](/how-cybercorps-works/compliance-architecture).
* What an ACE SAFE is as a security: [Security types](/building-on-the-protocol/reference/security-types).

## Good to know

* **The eligibility check keeps your identity private.** The non-U.S. proof shows your status without revealing who you are; recording it onchain costs a little gas.
* **An ACE SAFE is a security.** It is a claim on the company, recorded as a LET.
* **MetaLeX never holds your funds.** ACE uses the same onchain escrow as the rest of cyberRAISE.


# For holders: your securities

My holdings, LET pages and PDFs, transfers, scripify, conversion back from scrip and private sale drafts, from the holder's side

This page is for employees, advisors and investors who hold securities issued through MetaLeX. Two surfaces serve them, each reading different records:

* **My holdings** (`cybercorps.metalex.tech`) is the stakeholder portal. It shows the positions a company's cap table records for you, your Ledger Entry Tokens (LETs), documents the issuer shares with you, and your grants. You reach it by claiming a company's invitation link.
* **My Portfolio** (`cyberraise.metalex.tech`) is the investor view of what you invested through cyberRAISE: pending and closed EOIs and deals, your LETs and your scrip, with the actions on them (transfer, scripify, conversion back from scrip).

## Claim a stakeholder invitation

Companies onboard stakeholders with a private link, and the app never emails it. The claim page shows who the link was issued for and when it expires, then walks three steps: connect (or create) a wallet, prove you control it with a free Sign-In With Ethereum signature, and open your holdings. Claiming attaches that wallet to the company's existing record of you. The link cannot sign documents or move assets, and MetaLeX never asks for a seed phrase or private key.

Links expire after 14 days, and a company can revoke or regenerate one at any time, so ask for a fresh link if yours has lapsed. Claim only an invitation you expected.

## My holdings

After you **Authenticate**, **My holdings** lists the wallets it read (**Switch wallet** changes them) and shows a card for each company that has you on its cap table: how you are registered ("Registered as Alice Founder · employee"), your **Cap table positions** (class, units, status, vesting schedule and, if the issuer allows it, prices) and your **Onchain certificates**.

Each LET row gives its registered units and your wallet's role, a **PDF** download of the current certificate, and **View** for the LET's full page. The role is **registered owner** when your wallet is the holder of record, or **custody only** when your wallet holds the token while someone else is the registered owner. In that case the page notes that the quantities describe the registered position and are not assigned to your wallet. A current untokenized position offers **Statement PDF**, a position statement that says on its face it is neither a tokenized certificate nor a stock certificate.

Above the cards, one line per chain gives the indexer block the LET rows were read at and whether it is final, for example "Chain 8453: certificates read from the indexer at block …, not yet final (finalized through …); a reorg could still change what is shown." A LET minted moments ago appears once the indexer reaches its block.

The issuer decides what you see beyond your own issued positions and active LETs, which always show. Documents attached to your positions and per-unit price terms appear only if the company turned on those disclosures. Nothing here shows other stakeholders or company-wide ownership.

The page is read-only apart from **My grants**, which embeds the recipient view from [Token grants and onchain vesting](/using-the-apps/company/grants#for-grant-recipients), where you sign, claim and exercise.

## The public LET page

Every LET has a public detail page: the issuing company and its jurisdiction details, the security type and series, the investment amount, share terms and transfer restrictions for equity, the restrictive legends, and the rendered certificate. A **Registration and custody** block lists the **Registered owner** (the holder of record), the **NFT custody** wallet that holds the token, and the **Registered units**, with a note when part of the position is scripified. The two wallets usually match. They differ when someone holds the token in custody for its registered owner.

The rendered certificate follows the layout the contracts draw onchain: token ID and units at the top, the company name, "Ledger Entry Token" with an active or voided marker, the issuer and registered owner addresses linked to a block explorer, class and series, tiles for units, consideration and issue date, the authorizing officer, and the numbered transfer restrictions. Share classes show the consideration per share, and SAFEs, SAFTs and other convertibles show the whole amount. A voided LET is stamped VOIDED across its face.

Signed in as the LET's current holder or as an owner of the issuing company, you can **Download PDF**, a rendering of the current onchain certificate. The PDF says it is a generated rendering and not the onchain record itself, and gives the chain, contract and token ID to verify against.

Anyone with the URL can open the page. Whether your name shows depends on the choice made at issuance: encryption is optional, chosen in the privacy settings when you invest or by the issuer when issuing by hand. An encrypted name shows masked, with a prompt to sign in to decrypt, and decrypts only for the parties to the deal and the issuer. Sign in with the wallet that holds the LET to see your own. A name issued unencrypted appears in plaintext on the certificate and in its public metadata, with no sign-in gate. Signing officers' names are in the public metadata regardless of that choice.

## Act on what you hold

**My Portfolio** lists your pending investments (EOIs awaiting a decision), closed investments, your **Certificates** and your **Owned Scrips**. Finding rounds, expressing interest and countersigning tickets are covered in [cyberRAISE](/using-the-apps/cyberraise). The actions below work on what you already hold.

### Transfer a LET

A LET you are the registered owner of shows **Transfer ownership**. A transfer is a legal endorsement: you enter the recipient's address and name, sign a free endorsement, and confirm the transaction that endorses the LET to the recipient and moves registered ownership and custody together. The endorsement joins the LET's chain of title. The dialog checks whether the class and the LET allow transfers (issuer settings) while preparing the transfer, and stops if they do not, for example with "Registered-owner transfers are disabled for this lot."

If your wallet holds a LET in custody for someone else, the row offers **Return custody** instead. It returns the token to the registered owner without changing who is registered, and needs no endorsement.

Neither button appears on a voided LET, on one with units reserved onchain (for example, committed to a sale offer), or when the LET's registration, custody and terms do not read back completely.

### Scripify a LET

If the issuer has enabled scrip for the class, **Scripify** converts whole units of a LET into scrip, the fungible ERC-20 form, at the class's fixed ratio. Before you confirm the transaction, the dialog shows the ratio, the units available to scripify after any reservations, and your resulting scrip balance. Scrip can move in fractional amounts, subject to the token's own transfer rules, and your LET keeps correspondingly fewer units. Only the registered owner can scripify a LET.

A class can limit scripification to a whitelist of LETs, which the app recommends for private shares so that unvested or restricted shares cannot become transferable scrip. On such a class, scripifying a LET that is not listed stops with "This certificate isn't on the class's scripify whitelist. A company officer must add it on the Tokenization Hub before it can be scripified."

### Convert scrip back into a LET

Converting scrip back into registered units carries legal weight, because it makes you a registered holder again. There are two paths:

* If you still hold a LET of the same class, or the issuer has approved you, **De-scripify** opens **Convert scrip to a certificate**. Enter the **Scrip amount to convert** and choose **Review conversion**. The review shows where the units will land (your existing LET, which the protocol picks as the first non-void LET you hold of record, or a new LET under the issuer's approval), the units added, your total registered units afterwards and the scrip remaining. **Confirm conversion** sends the transaction. The amount must be at least the class's de-scrip minimum.
* Otherwise, **Request de-scripification** sends the issuer an offchain request through a free, SIWE-authenticated form. The company reviews it from the Tokenization Hub, and once it approves, your button becomes **De-scripify**.

What the review checks depends on the company's version:

* **On a v5 company**, the review checks the conversion against the class's activated legal terms (see [Legal classes, series and versioned terms](/using-the-apps/company/captable#legal-classes-series-and-versioned-terms)). If the company has not approved and activated those terms, the review stops with an explanation, and the company has to act first.
* **On a v4 company**, the LET contract has no protocol class, so there are no activated terms to check. The review shows the company's onchain terms for the destination instead: the terms on your LET, or the terms in the issuer's approval. The protocol also picks the LET differently. It takes the first non-void LET that is both in your wallet and registered to you. A LET registered to you that another address holds, such as an escrow, does not count. Conversion works on the current v4 release of the company's issuance manager. On an earlier v4 release, the review stops with an explanation, and the company can upgrade to v5.

The app refuses the conversion if the issuer has frozen your scrip. The dialog notes that the conversion creates a tokenized cap table position, and that the company's governing documents decide whether that position is part of its securities ledger.

### Transfer scrip

Scrip rows offer **Transfer** when the token's compliance rules allow it, a plain ERC-20 transfer to the recipient you name.

### Recall an expired EOI

If you bid into a founder-approval round and your offer expired unanswered, the EOI row offers **Recall**, one transaction that returns your escrowed funds.

> **Under the hood.** A LET transfer calls the LET contract's `endorseAndTransfer`, which records the endorsement and moves the token. Scripify and conversion back are [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager) calls that move units between the [LET](/building-on-the-protocol/reference/contracts/ledgerentrytoken) and [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip), the class's scrip token. Why re-registration needs the issuer's approval is explained in [LETs and scrip](/how-cybercorps-works/lets-and-scrip).

## Prepare a private sale (v5 companies)

Below the certificate on a LET's page, **Private sales** opens a workspace for selling some or all of the LET's units to one buyer you name. The workspace prepares and reviews a sale. It cannot post the onchain offer.

The registered owner (the holder of record, not a custody-only wallet) chooses **Prepare sale draft** and enters the **Named buyer address**, **Units to sell**, **Payment token address**, **Total consideration (token units)**, and the **Seller party values** and **Buyer party values** for the sale agreement, one value per line. Delivery goes directly to the buyer's wallet. **Save private draft** stores the draft, visible only to the two named parties when signed in. The app sends no invitation, so tell the buyer yourself. Saving reserves no units. A saved draft's terms cannot be edited, so a change means a new draft and new reviews.

Each party reviews the draft card (exact units, consideration, delivery and the original agreement), ticks the acknowledgement and clicks **Record my review**. That records an app acknowledgement, which is neither a signature nor a transaction authorization. The seller can **Cancel draft**. If the LET changes after the draft was saved, the review is refused and a fresh draft is needed.

When both parties have reviewed, the card reads "Both parties reviewed · submission unavailable" and its control reads **Post sale unavailable**. Posting needs the company's deal manager to be a v5 contract set up for named-buyer sales: an enabled exemption pathway, a buyer-approval condition among the conditions every sale must satisfy, a designated approver, a separate closing condition at which a fill can be held or released, and approval helpers whose code the app has reviewed for that chain. Hovering over the control names the first missing piece. On a v4 company it says the deal manager reports a different protocol version. The app has no reviewed approval-helper deployment recorded for any chain, so no company passes this check, and it has no buyer acceptance or officer approval screen.

The workspace opens with the limit a seller should understand before any sale is posted: "Approving a buyer does not approve their terms." The company's approval names who may accept. It does not fix the units, payment or party values the buyer accepts on. A fill that departs from what you reviewed can be stopped only by the company's designated approver withholding its release, or by the buyer agreeing to void it, never by you alone.

**Check an existing sale or recover an interrupted operation** reads an existing offer's status onchain from its deal manager address and offer ID. Its action buttons are disabled.

## Good to know

* **Which signatures cost gas.** SIWE sign-ins, de-scrip requests, endorsement signatures and sale-draft reviews are free. Transfers, custody returns, scripify, conversion back from scrip, claims, exercises and recalls are transactions. PDF downloads are free and need only a sign-in.
* **Your wallet is the key to everything.** Holdings, grants and decryption follow the wallet the issuer has on file for you. If something you expect is missing, check that you are connected with the right wallet.
* **There is no order book.** MetaLeX has no built-in secondary market. Transfers and scrip are the rails on which privately negotiated trades settle, and each private sale draft names one buyer.


# Your profile

Your MetaLeX identity, linked wallets, visibility settings, notifications and Safe signing delegation

Your **profile** (`profile.metalex.tech`) is your MetaLeX identity, and every app reads it. When a founder reviews your expression of interest, or your name appears on a certificate, the details come from here. The profile belongs to your MetaLeX account, which can hold **several linked wallets**, so it stays the same when you switch between them. The cyberCORPs app, cyberRAISE, cyberSign and LeXcheX share one account card and menu, and show the same profile and wallets.

A profile is **optional but recommended**. You can stay anonymous.

## Create your profile

1. Connect a wallet and **Sign in**. Signing in is a free signature that costs no gas, and it creates your MetaLeX account and its profile record.
2. Pick a profile picture. If you don't have one, the preset gallery opens on its own (see [Choose a preset picture](#choose-a-preset-picture)).
3. Enter a **Name** (up to 25 characters) and **Continue**.

**Stay Anon for now** skips the whole step.

![Profile onboarding](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-39e473f4220d4151f5a1abc954b72791ac59a305%2Fprofile-onboarding.png?alt=media)

If a company onboarded you as a stakeholder through an invitation link, your name prefills from the stakeholder record. It starts out visible only to companies you hold securities in, until you choose to make it public.

## Edit your profile page

Your profile page (*Account Profile*) is a public card with your picture, name, bio, social links, your **LeXcheX accreditation status** and your **MetaLeX account addresses**: every wallet linked to the account, plus any multisigs. A founder's title (e.g. "Founder of …") comes from their cyberCORPs onchain and appears automatically. Your own profile also shows a public **platform activity** card: your cyberCORPs and how long you've been cybernetic if you're a founder, and your closed investments if you're an investor.

To edit it, sign in (**auth to edit profile**) and open the edit form:

* **Your PFP**: **Upload picture**, **Choose MetaLeX Pfp** from the preset gallery, or **Import Your X Pfp** (**Use X Pfp** once your X account is connected).
* **Profile identity**: your **Display Name** (up to 25 characters) and bio. Others on MetaLeX see the display name as far as your visibility setting allows. A nickname or pseudonym is fine and need not match the legal name you use to form a company.
* **Private formation details**: your legal first and last name, phone number, country of residence and mailing address, for when you act as a company's responsible party. Only you see them; profile visitors never do. They are copied into a formation request only when you choose **Reuse profile details** there (see [Form a company](/using-the-apps/formation)). If you stay linked as the company's first public officer, your legal name also fills that officer's onchain record, which is permanently public; you can review it before publishing.
* **Accreditation**: your LeXcheX status, with a link to [LeXcheX](/using-the-apps/lexchex) to get accredited. Public rounds stay closed to you until you are. Once you are accredited, the section shows a validity countdown with **view certificate** and **renew certificate** links.
* **Socials**: **Verify X Account** through X's own sign-in, and **Verify Telegram Account** through the Telegram login widget. If verification fails, the form says why: the X account is already linked to another MetaLeX account, X declined the authorization, or "X verification isn't available here" when X sign-in isn't enabled. This is also where you turn on [Telegram notifications](#get-notifications-on-telegram).
* **Additional Details**: website and additional info.
* **Privacy**: name, socials, website and additional info each have a visibility setting: public, private, viewable to all founders, or viewable to connected founders only. Your picture and bio are always public.

Saving your profile is a plain save with no transaction or gas. The public profile and the private formation details save separately; if only one of them saves, the form says which and keeps your edits.

### Choose a preset picture

The preset gallery (*Pick a PFP that fits your role and style*) has more than 80 pictures, most of them designs of LeXbot, the MetaLeX mascot, grouped by theme: Ethereum, Solana, Zcash, Bitcoin, MetaDAO and others. Any account can use any preset, and none is reserved for one person. Clicking a picture opens an enlarged preview. Step through the gallery with the arrows or your arrow keys, then choose **Use this PFP**, or **Keep this PFP** if it is already yours. Closing the preview returns you to the gallery without changing anything.

## Use several wallets with one account

Your profile belongs to your account. The wallet you have **active** signs and sends transactions, and switching it never changes which profile you are signed into. The account card in the header shows both the signed-in profile and the active wallet. A **!** marker on the card means the active wallet isn't linked to your profile.

Clicking the card opens the account menu: **Profile**, **Wallet Settings**, the wallets connected in this browser (click one to make it active), **Link another wallet** and **Sign out**. In the cyberCORPs app and cyberRAISE the menu also has **Your Multisigs**, **Delegation Settings**, **My grants** and **My formation orders**. From cyberSign and LeXcheX, **Profile** and **Wallet Settings** open the profile app in a new tab.

### Link a wallet

**Link another wallet** asks you to connect a wallet and sign a short authorization message with it. Selecting a connected wallet that isn't linked yet (**Authorize** on its Wallet Settings row) shows your profile and the wallets already on it, and **Link wallet** asks for the same signature. The signature is free and single-use, and it expires after a few minutes. A wallet already authorized for your profile isn't asked again. The wallet itself has to sign, so a smart-contract wallet such as a Safe can't be linked this way; to act for a Safe, use [delegation](#delegate-signing-for-a-safe).

A wallet that belongs to a different MetaLeX profile can't be linked to yours, and profiles are never merged. If your sign-in has picked up such a wallet, a **Wallet link needs attention** notice explains that it can't authorize payments or account changes for this account. If the other profile is yours, unlink the wallet in Wallet Settings, then use **Sign in with …** to sign in with that wallet and reach that profile (**Stay on this account** keeps you where you are). If it isn't yours, or it is the only wallet on the account, contact support.

If a wallet you sign in or link with carries a profile that no MetaLeX account has claimed, that profile, with its history, is recovered into your account automatically, and linking the wallet shows **Profile recovered**. A wallet you have unlinked from your account is not recovered this way.

An **Authorize your wallets** prompt may point you to Wallet Settings to choose the wallets for your profile and sign a fresh authorization for each. **Later** dismisses it. Your existing profiles and their records stay unchanged either way.

### Manage wallets in Wallet Settings

**Wallet Settings**, in the account menu, lists your **Linked Wallets**, the active one first, and any **Unlinked Wallets** connected in this browser, with **Link another wallet** at the bottom. Each row is labeled with the wallet's type and status, for example *active · profile linked*:

* *not connected* marks a linked wallet that isn't connected in this browser;
* *login linked · profile unverified* marks a wallet your sign-in knows about but your profile hasn't authorized, and **Authorize** adds it;
* *another profile* marks a wallet that belongs to a different profile.

A linked wallet can be unlinked only when at least one other linked wallet remains on the account. An *embedded* wallet created by MetaLeX as part of your profile can't be unlinked. Unlinking takes effect at once: the wallet stops giving this account access to company workspaces, private documents and Safe-owner permissions. If it was the active wallet, another connected linked wallet becomes active. Linking it again needs a fresh authorization signature.

When the active wallet is your embedded wallet, Wallet Settings also shows **Embedded wallet confirmation prompts**, where you choose whether transactions and signatures skip the confirmation popup. The wallet still signs in your browser with your key; only the popup is hidden.

## Get notifications on Telegram

MetaLeX sends notifications over **Telegram**. In the profile's Socials section, **Enable notifications** opens a private chat with the MetaLeX bot. Once you start it, the bot messages you about your offers, deals and investments: offers submitted, accepted or rejected; deals ready to sign or finalized; certificates assigned or transferred; agreements ready and fully signed; rounds closing. Each message links to the relevant page. In the chat, `/status` shows your subscription and `/stop` ends it.

Inside the apps, the cyberRAISE sidebar shows counters too: on **Portfolio** for investors, and on **Raise** for founders with pending EOIs.

## Delegate signing for a Safe

The **delegation** page is for people who sign for a **Safe multisig**. Collecting multisig signatures for every legal agreement is slow, so a Safe can grant agreement-signing authority to a single wallet.

Pick the chain, enter your Safe address (a multisig that lists you as an owner) and set an expiry date, which defaults to about six months out. **Sign delegation** proposes a Safe transaction, which you complete in your Safe's own interface, where your co-signers approve it. The delegated wallet can then sign cyberAgreements for the Safe until the delegation expires.

> **Under the hood.** Delegation registers the delegate with the protocol's agreement layer, so a signature from the delegate counts as a signature from the Safe when a cyberAgreement executes. Background: [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry) and [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement).

## Good to know

* **Your profile follows your account.** Act from any linked wallet and it's still you. A wallet that isn't linked to your account has its own profile or none; link it in Wallet Settings. The LeXcheX credential is the exception: it stays in the wallet that holds it (see [LeXcheX](/using-the-apps/lexchex)).
* **Company access follows your profile too.** If any wallet linked to your profile owns a company, you can open and work in that company's workspace while another linked wallet is active, or with no wallet connected. Signatures and transactions for the company still need an authorized wallet on the company's network. When the active wallet isn't one, the app says "Your profile has access to this company. Company signatures still require an authorized wallet and network.", names the wallets to use, and offers **Switch to …** and **Connect signing wallet**. Access is checked against the chain again as you work, so it ends when the wallet loses its role or you unlink it.
* **What cyberSign shows.** The other parties to an agreement see your picture and, if it is public, your name; a name visible only to founders stays hidden there. cyberSign fills your name into an agreement only from a public profile name, and you can edit or clear it before signing.
* **Verified socials help when you invest.** Verified socials and accreditation build trust with founders reviewing your offers in founder-approval rounds. Founder profiles without a verified social carry a "no verified socials" warning for investors.


# LeXcheX

Prove accredited-investor status onchain without revealing your balances

Many private investment rounds are open only to **accredited investors**. **LeXcheX** (`lexchex.metalex.tech`) lets you prove that status from your onchain wealth and carry the result as a credential the other MetaLeX apps can check.

![LeXcheX](https://1721738507-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FeGVSB1YyS74wdU2MBU9q%2Fuploads%2Fgit-blob-3a268d09ca4ab3cb3d7dba13c45e0a2a16036745%2Flexchex-home.png?alt=media)

## When you need a credential

A publicly advertised [cyberRAISE](/using-the-apps/cyberraise) round is open only to accredited investors, so you need a valid LeXcheX credential before you can invest in one. The cyberRAISE public-rounds list and your [profile](/using-the-apps/profile) both show your accreditation status and link here. Once you hold a credential, every round that requires it checks it automatically.

The credential is a **soulbound NFT**: a non-transferable certificate bound to the wallet that earned it. It can't be sent, sold or moved to another wallet, and it proves that the wallet's holder is accredited.

## Get accredited in three steps

1. **Questionnaire.** Say whether you are applying as an **individual** or a **legal entity**, confirm your legal status, choose your evaluation path, and complete the compliance form, which follows SEC Rule 501(a). The wallet-based paths are **net worth of $1M+** for individuals and **total assets of $5M+** for entities. The form marks its other paths coming soon.
2. **Evaluation.** Connect and verify the wallets that hold your assets by signing a verification message from each, and LeXcheX values the portfolio across them. You can combine several wallets, and if the first evaluation falls short you can add or swap wallets and try again. Plain wallet holdings and DeFi positions both count; very small balances, borrowed positions and tokens below a market-cap floor do not.
3. **Sign Agreement.** Sign the LeXcheX agreement from your wallet, and **Mint Certificate** mints the accredited-investor certificate to that wallet.

An **Income** path, marked **ALPHA**, lets an individual prove $200k+ annual income through a Plaid bank connection instead of wallet assets.

## Other routes to accreditation

As the cyberRAISE public-rounds list explains, an investor can also qualify by **investing above a threshold** (around $200k for an individual, $1M for an entity) or through **manual verification** by a MetaLeX attorney outside the app. Which route fits depends on your circumstances.

## Keep and renew the credential

* It stays in your wallet, and restricted rounds verify it automatically, so you don't repeat the process for each one.
* It is tied to the wallet that holds it. Invest from a different wallet and that wallet carries no credential. Your [profile](/using-the-apps/profile) follows your account across all its linked wallets, but the credential stays put.
* **Credentials expire.** A certificate is generally valid for about three months. Your certificate page shows its status (active, expired or voided) and the time remaining, with a **Renew Certificate** button once it lapses.

## Your MetaLeX account in LeXcheX

LeXcheX uses the same MetaLeX account and account card as the other apps. Clicking the card opens the account menu: **Profile**, **Wallet Settings**, the wallets connected in this browser, **Link another wallet** and **Sign out**. **Profile** and **Wallet Settings** open the [profile app](/using-the-apps/profile#use-several-wallets-with-one-account) in a new tab, and **Sign out** ends your MetaLeX session as well as the wallet connection. The notices the other apps show about your wallets, such as a wallet that belongs to another profile, appear here too.

Linking or unlinking wallets on your profile never moves a credential. It stays in the wallet that earned it.

## Under the hood

A LeXcheX credential is an onchain accreditation NFT issued by the protocol's [`LexChex` / `LexChexMinter`](/building-on-the-protocol/reference/contracts/lexchex) contracts. A round that requires accreditation attaches a **`lexchexCondition`**, an onchain [condition](/building-on-the-protocol/reference/conditions) that checks for a valid LeXcheX credential before an investment goes through. [Compliance architecture](/how-cybercorps-works/compliance-architecture) explains how this fits into Reg D and Reg S gating.

## Good to know

* **Onchain wealth counts.** LeXcheX exists so that assets held in your wallet can support accreditation.
* **The credential shows the test you passed and hides your balances.** The certificate publicly shows which evaluation you passed and when it expires, never your holdings or their amounts.
* **LeXcheX gives no legal or financial advice.** Accreditation rules vary and change, and whether you qualify is a legal question.


# Launchpads: MetaDAO and Umia

Form the entity a MetaDAO or Umia launch prescribes, and browse the companies each launchpad has formed

Some token launchpads prescribe the legal entity a project must use, and [MetaDAO](https://metadao.fi) and Umia are two of them. For each, the cyberCORPs app has a single-purpose page that forms the prescribed entity as a cyberCORP: a segregated portfolio of a Cayman Islands segregated portfolio company. Each page forms the entity once and does nothing else. Governance of the entity, such as MetaDAO's futarchy, happens on the launchpad's own platform.

If you are still deciding how to structure a project, the entity guide in the cyberCORPs app's formation flow asks whether a launchpad will prescribe your entity structure, naming MetaDAO and Umia as examples. If one will, the guide sends you to that launchpad's own onboarding, because the C-Corp and LLC formation form does not create the launchpad's vehicle (see [Form a company](/using-the-apps/formation)).

## How you reach the formation pages

You arrive from the launchpad's own launch flow, through a link that carries your token and enterprise details. Opening a page without those details shows an error.

## What the two pages ask for

Each page presents a **formation agreement** for your entity, shown beside the form (*Contract Preview / Reference*). A banner says the prefilled values came from the launchpad's form. Both pages lock the enterprise name, company name, company type and jurisdiction. They differ in these ways:

|                          | MetaDAO                                                                | Umia                                                                         |
| ------------------------ | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Company name             | *{enterprise} S.P., a segregated portfolio of Futarchy Governance SPC* | *{enterprise} S.P.*                                                          |
| Company type             | Segregated Portfolio of Segregated Portfolio Company                   | Segregated Portfolio of Umia Launcher SPC, a Segregated Portfolio Company    |
| Jurisdiction             | Cayman Islands                                                         | Cayman Islands                                                               |
| Network                  | Base                                                                   | Ethereum                                                                     |
| Access                   | anyone with the launch link                                            | password-protected (below)                                                   |
| Token name and ticker    | locked                                                                 | editable                                                                     |
| Founder/operator address | the wallet you connect                                                 | the address in the Umia link, or the wallet you connect if the link has none |

On both, you fill in or confirm the **founder/operator name and contact details**.

### Unlock the Umia form

An access password protects the Umia page. A link from Umia can unlock it directly. Otherwise the page shows **UMIA Form Access**, where you enter the password you were given and **Continue**. Either way, the browser keeps access for 24 hours, which covers the formation.

## Form the entity

1. Arrive from the launch flow (the page opens with your details filled in), unlock the Umia form if asked, and **connect your wallet**.
2. Check the editable fields: the founder/operator name and contact details and, on Umia, the token name and ticker.
3. **Sign the formation agreement**, a free signature.
4. **form cyberCORP.** MetaLeX submits the formation transaction and pays its gas, so you pay nothing. The page waits for the transaction to confirm before moving on, and shows *Error forming CyberCorp* if it fails.
5. A **Formation Summary** page confirms the cyberCORP was minted. It shows the entity's details, the operator account and the network, links to the executed **Formation Agreement** and **Board Resolutions**, and links to the entity's public company page (see [Run your company](/using-the-apps/company)). Bookmark it. **Back to MetaDAO** or **Back to UMIA** returns you to the launch.

On MetaDAO, the summary data can trail the chain by a minute right after formation. When it does, the page says the summary is still syncing and asks you to refresh; the formation has already succeeded.

After formation you manage the entity like any other cyberCORP: issue securities from the cyberCORPs app (see [Run your company](/using-the-apps/company)) and raise in [cyberRAISE](/using-the-apps/cyberraise).

## Browse the launchpad directory

`cybercorps.metalex.tech/company` is a public directory of the companies formed through one launchpad at a time, MetaDAO or Umia, switched at the top of the page. It lists them newest first, 50 per page, with a name search. Each row shows the entity type, jurisdiction, chain and deployment date, and links to the company's public page. A launchpad company's public page links back to its list (**All MetaDAO companies**, **All Umia companies**). Companies formed any other way have a public page but are not listed.

## Under the hood

Submitting forms a cyberCORP structured as a segregated portfolio of a Cayman segregated portfolio company and anchors two documents in the same transaction: the formation agreement and the board resolutions. MetaDAO formations go through the **MetaDAO / SPC** path and Umia formations through the **ParentCoFactory**. See [Factories](/building-on-the-protocol/reference/factories), [Deploy a MetaDAO SPC](/building-on-the-protocol/how-to/deploy-metadao-spc), and, for how one legal entity can hold several independently governed portfolios, [Legal mappings across jurisdictions](/how-cybercorps-works/legal-mappings).


# Overview

Why the protocol is designed as it is, what it relies on in law, and how far MetaLeX's control over a deployed company reaches.

These pages explain the design and its legal grounding for lawyers, investors and integrators deciding whether the model fits. They argue why a company's onchain records can be its legal record, why each security has two token forms, where compliance runs, and what MetaLeX can and cannot do once a company is deployed. To build on the protocol, start with the [guides](/building-on-the-protocol/how-to), which also list the contracts each product uses, and the [reference](/building-on-the-protocol/reference).

* [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer) is the thesis: where a company's governing documents designate the onchain records as its securities ledger, changing onchain state is the legal change.
* [Ledger Entry Tokens and scrip](/how-cybercorps-works/lets-and-scrip) shows how a Ledger Entry Token (LET) records a holding on the register while scrip, its fungible counterpart, trades without moving it.
* [Legal mappings across jurisdictions](/how-cybercorps-works/legal-mappings) sets out the statutes and governing documents that anchor the register for Delaware corporations and LLCs, Cayman and BVI entities, English companies and funds.
* [Compliance architecture](/how-cybercorps-works/compliance-architecture) covers condition contracts, credentials, and the choice of where in the scrip lifecycle the strictest gate sits.
* [Composability and DeFi](/how-cybercorps-works/composability) weighs what scrip can do in AMMs and lending markets against what its restrictions cost.
* [Upgrades and what MetaLeX controls](/how-cybercorps-works/upgrades-and-control) explains opt-in upgrades, fees and templates, and the owner role the deploying factory keeps.
* [Regulatory context](/how-cybercorps-works/regulatory-context) maps the US exemptions and SEC staff statements the protocol is built around.


# Constitutive vs. pointer tokenization

Why a cyberCORP's onchain records can be its legal securities ledger, and what the company's governing documents must say for that to be true.

Most tokenized securities are pointer tokens. The record that legally controls who owns what sits with a transfer agent, on Carta or in a law firm's files, and the token refers to it. Moving a pointer token asks whoever keeps that record to update it, and the issuer or its agents may decline. Take the chain away and the legal position does not change.

cyberCORPs is built for constitutive tokenization, in which the onchain records are the securities ledger and changing onchain state is the legal change itself. For the securities this covers, the chain and the legal record cannot drift apart, because there is no offchain copy of the register to reconcile and no transfer agent to instruct.

## The governing documents decide which record is the ledger

Whether the chain is a company's securities ledger is settled by its governing documents: the certificate of incorporation and bylaws, an operating agreement, articles of association, a partnership agreement, or a fund's constitutional documents. They can designate the protocol's contracts as the official register for some or all of the company's securities. Tokenized units and onchain data do not settle the question on their own. Without a designation, Ledger Entry Tokens (LETs) record the same facts, but the legal record is wherever the documents put it. A company brought onchain from Carta, Pulley or a spreadsheet usually keeps its securities ledger where it was, and the app's [cap table](/using-the-apps/company/captable) tracks it.

The MetaLeX-form bylaws of Delaware corporations formed through the cyberCORPs app show what a designation looks like. Shares are uncertificated unless the board decides otherwise. The board designates which classes or series are Tokenized Shares, and the smart contracts that record them, with their state and event logs, become the stock ledger for those shares. Any other shares stay on an offchain stock ledger, and the two parts together form the corporation's stock ledger under DGCL §§219 and 224. Record ownership of a Tokenized Share changes only through an operation of the onchain system: editing a LET's metadata, minting or voiding a LET, or minting or burning scrip tied to a LET. Moving a token from one address to another changes nothing by itself.

## Corporate law permits the designation

Delaware has the most fully worked-out basis. DGCL §224 lets a corporation keep its records, the stock ledger included, on electronic networks or databases, distributed ones among them, provided the records can be converted into clearly legible paper form and carry the information other sections require. §158 permits uncertificated shares, §202 governs how transfer restrictions are noted, §155 authorizes scrip, and §219 draws the stockholder list from the stock ledger. The [legal mappings](/how-cybercorps-works/legal-mappings) page sets out each provision against the protocol object that uses it.

Delaware LLC law, Cayman corporate and SPC regimes, BVI corporate law, English company law and partnership and fund statutes reach the same result through comparable provisions or through the entity's own contracts. The protocol assumes no particular statute. It needs only a governing law that lets the entity's documents designate the onchain register as authoritative, and the `CyberCorp` contract records the entity type and jurisdiction as configuration.

## A LET is the holder's entry in the official record

Where the designation exists, a LET is the holder's interest as the company's official records show it, and the register for those securities can be read straight from the chain. An auditor, a regulator or a court can ask the chain who the holders of record are and get the legal answer. Scrip, the fungible form, relates to that register differently, and [Ledger Entry Tokens and scrip](/how-cybercorps-works/lets-and-scrip) explains how.

## Further reading

* MetaLeX, ["What Is Slop Tokenization and Why Is It Bad?"](https://metalex.substack.com/)
* MetaLeX, ["5 Ways of Tokenizing Securities (& One Which Is Best)"](https://x.com/lex_node/status/2030322576208068616)
* SEC staff, [statement on tokenized securities](https://www.sec.gov/newsroom/speeches-statements/corp-fin-statement-tokenized-securities-012826) (January 2026)


# Ledger Entry Tokens and scrip

How a Ledger Entry Token records a holding on the register, how scrip trades apart from it, and where the two meet.

A register entry has to name a holder, a unit count and the restrictions that bind those units, so it cannot be fungible. AMMs, lending markets and vesting contracts expect interchangeable ERC-20 units. The protocol gives each job its own token. A **Ledger Entry Token (LET)** is an ERC-721 token that records one entry on the company's register. **Scrip** is an ERC-20 token that a holder creates by moving units out of a LET and can convert back into a LET.

## A LET is one entry on the register

Each class or series has its own LET contract, a deployment of [`LedgerEntryToken`](/building-on-the-protocol/reference/contracts/ledgerentrytoken). A LET carries what the governing law asks of a register entry, namely the holder's name, the unit count, the class or series, restriction legends, the endorsement history, authorized signatures, the acquisition price and the URI of the governing agreement.

The LET contract records the holder of record separately from the address that holds the ERC-721 token, so moving the token leaves the registration where it was. Registration changes only through operations the company gates, such as issuance, trade settlement, endorsement, voiding and the conversion of scrip into a LET. On a v5 company, possession of a LET also carries no power to endorse it. Only the holder of record endorses, or the IssuanceManager does so as registrar on the holder's signature, so a custodian holding the token cannot endorse it to itself and take title on delivery. A v4 LET contract also accepts an endorsement from the address holding the token. Corporate, LLC, partnership and fund law all distinguish possessing an instrument from being registered as its owner, and the LET contract keeps the two apart.

## Scrip carries units off the LET

`scripifyCert` moves units from a LET into scrip, and `convertScripToCert` turns scrip back into a LET. Each LET contract can have one scrip contract ([`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip)), which mints at the scrip ratio set for that LET contract. Scripifying can be partial, so a holder of 1,000,000 shares can scripify 250,000 to trade and keep 750,000 active on the LET. The [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager) keeps an ERC-4626-style pool for each LET contract that tracks every LET's scripified units as a vault position, and a conversion back into a LET withdraws from that pool in proportion. Every unit of scrip in circulation is backed by units scripified out of LETs in the same LET contract.

## Scrip's legal character comes from the governing documents

The company issues scrip through its own IssuanceManager, against units taken off its own register, and sets the conditions for converting it back. No third party issues the scrip or holds what backs it. In a Delaware corporation, DGCL §155 authorizes scrip, and the governing documents decide what rights it carries. MetaLeX-form bylaws say scrip tokens are not stock and, unless their terms provide otherwise, represent neither equity nor debt of the corporation. Holding scrip confers no stockholder right by itself (no vote, dividend, liquidation, appraisal or inspection right), though the scrip's terms may provide, as §155 permits, for distributions on the backing shares to be escrowed and released.

Under those bylaws, the holder of record of a LET remains the registered owner of the units it scripified, the scrip holders carry the economics of those units, and a scrip holder becomes a stockholder of record by converting into a LET. The app's [cap table](/using-the-apps/company/captable) shows both readings. Its **Registered** view credits the LET holder, and for an equity class its **Beneficial** view credits the scrip holders.

## Inside the scrip lifecycle, the register moves only at the two conversions

Scrip can trade in a Uniswap v4 pool all day while the register stays still. When a scrip holder wants to be the holder of record, they convert, and at that moment the full gate runs, made up of the conversion conditions set for that LET contract and, for a new holder, the issuer's recertification approval. Scripifying runs its own conditions on the way out. Primary issuance, secondary settlement, endorsement and voiding change the register through their own gated flows.

```mermaid
flowchart LR
    subgraph REG["Register (legal ownership)"]
        LET["LET (ERC-721)<br/>holder of record · units · legends"]
    end
    subgraph DEFI["DeFi layer (possession)"]
        SCRIP["Scrip (ERC-20)<br/>fungible, composable"]
        POOL["AMMs · lending · collateral"]
    end
    LET -- "scripifyCert(units)<br/>scripify conditions" --> SCRIP
    SCRIP <--> POOL
    SCRIP -- "convertScripToCert(amount)<br/>conversion conditions ·<br/>recertification approval for new holders" --> LET
```

Because the register moves only at those two conversions, a company can choose where compliance runs. A whitelisted LiquiLeX pool checks credentials on every swap. An open pool leaves trading free, optionally screened with zkPassport, and runs the full gate when scrip converts into a LET. [Compliance architecture](/how-cybercorps-works/compliance-architecture) covers both models.

## A secondary trade can settle on the register or in scrip

cyberTRADE settles negotiated secondary trades by either route. On the register route, a v5 company's DealManager runs an offer flow in which the buyer elects an exemption pathway, the pathway's conditions and the issuer's own conditions run, and settlement reduces or uses up the seller's LET and mints the buyer a new LET carrying the seller's endorsement. On the scrip route, the trade settles in scrip, on an AMM through LiquiLeX or otherwise, and the buyer converts into a LET later if they want to be on the register.

## See also

* [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade)
* [Issue a LET](/building-on-the-protocol/how-to/issue-a-let)
* [Composability and DeFi](/how-cybercorps-works/composability)


# Legal mappings across jurisdictions

Which statutes and governing documents anchor the onchain register for Delaware corporations and LLCs, Cayman and BVI entities, English companies and funds.

The contracts encode no jurisdiction. `CyberCorp` stores the entity type and jurisdiction as configuration, and the same contracts serve a Delaware C-corp, a Delaware LLC, a Cayman SPC, a BVI fund and an English company. What changes from one regime to the next is the law that lets the entity designate the onchain register and the document that does it.

Under any regime, the onchain register is the entity's legal record only when two things hold. The governing law must let the entity's governing documents designate an external record-keeping system as authoritative, and the documents must in fact do so, identifying the cyberCORP contracts by address or by registry reference. Most developed corporate, LLC, partnership and fund regimes allow the designation. Writing it is a drafting task, and for Delaware corporations formed through the cyberCORPs app, MetaLeX's form bylaws do it.

## Delaware corporations

Delaware is the worked example. Each protocol object relies on a DGCL provision:

| Protocol object                                      | DGCL provision                                                                                                                                                                     |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Onchain register                                     | §224: records, the stock ledger included, may be kept on electronic networks or databases, distributed ones among them                                                             |
| Classes and series, authorized share counts          | §151                                                                                                                                                                               |
| Ledger Entry Tokens (LETs) for uncertificated shares | §158 permits uncertificated shares. The holder notice §§151(f), 156, 202(a) and 218(a) require can be carried in LET metadata where the bylaws allow it, as MetaLeX-form bylaws do |
| Restriction legends                                  | §202                                                                                                                                                                               |
| Scrip                                                | §155                                                                                                                                                                               |
| Stockholder list                                     | §219, drawn from the stock ledger                                                                                                                                                  |

MetaLeX-form bylaws make the onchain system the stock ledger for the shares the board designates as Tokenized Shares and keep any other shares on an offchain stock ledger. The two parts together are the corporation's stock ledger. [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer) describes that structure.

## Other entities and jurisdictions

| Entity                                 | Document that designates the register                             | What the LET fields map to                                                                                                                                                                                       |
| -------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Delaware LLC                           | Operating agreement                                               | Whatever the operating agreement requires for each membership-interest entry. Delaware LLC law gives operating agreements broad latitude over how interests are recorded, so no §224 analogue is needed.         |
| Cayman LLC or SPC                      | Constitutional documents (memorandum and articles, LLC agreement) | The register the constitutional documents define. Cayman LLC and SPC statutes accommodate the designation by contract.                                                                                           |
| BVI company or fund                    | Articles of association, or the fund's constitutional documents   | The share-register requirements of the BVI Business Companies Act 2004, or the fund's constitutional equivalents.                                                                                                |
| English company                        | Articles of association                                           | The register-of-members requirements of Companies Act 2006 §113. CREST's regime for uncertificated shares is separate. For the purposes of the constitutional documents, the cyberCORP register is the register. |
| Fund (LP, LLC or other fund interests) | Partnership agreement or fund LPA                                 | The interest entries the agreement requires.                                                                                                                                                                     |

LLC membership interests use the same `ShareExtension` as stock, configured for the LLC's class structure. A Cayman SPC uses `MetaDAOFactory`, or a custom factory, to model its segregated portfolios as sub-entities that share a parent cyberCORP. A fund can model capital commitments, calls and distributions with the existing contracts: a LET for each LP unit, scrip for a tradable fund interest, and the `DealManager` for capital calls.

## See also

* [Regulatory context](/how-cybercorps-works/regulatory-context)
* [Agreement templates](/building-on-the-protocol/reference/templates)
* [Factories](/building-on-the-protocol/reference/factories)


# Compliance architecture

Where compliance runs: condition contracts on every legally significant transition, the credentials they read, and the point where scrip becomes a Ledger Entry Token again.

The contracts carry no compliance regime of their own. Each company builds its regime (Reg D, Reg S, a local private-placement regime, qualified-purchaser rules, sanctions screening) out of condition contracts that gate state changes, credential registries those conditions read, and a choice of where in the scrip lifecycle to put the strictest gate.

## Conditions gate every transition that changes legal state

Issuance, scripification, conversion of scrip back into a Ledger Entry Token (LET), deal close, round acceptance, and the posting and settlement of secondary offers each accept condition contracts (`ICondition`, or its typed variant for secondary trading). Each condition is evaluated at the moment of the state change, and conditions compose, so one transition can require several facts at once. A condition can require that an investor holds a valid LeXcheX accreditation credential, that a buyer has a zkPassport proof of non-US nationality, that a recipient holds a soulbound membership NFT, or that the transaction comes after the block in which the company filed its Form D.

On a v5 company, secondary trades get the fullest set. A buyer accepting an offer elects an exemption pathway (Rule 144, §4(a)(7), §4(a)(1½), Rule 144A or Reg S), and the trade runs the conditions wired to that pathway, alongside any conditions the issuer applies to every trade. The pathway conditions cover holding periods, disclosure, distribution compliance, buyer eligibility, holder caps, state of residence and CFIUS-style blocked jurisdictions. A pathway the issuer has not configured blocks every trade under it. [Conditions](/building-on-the-protocol/reference/conditions) lists the contracts.

## The strictest gate can sit where scrip becomes a LET

Inside the scrip lifecycle, the register changes only when units are scripified out of a LET or scrip is converted back into one (see [Ledger Entry Tokens and scrip](/how-cybercorps-works/lets-and-scrip)). A company can therefore run its full gate at conversion and let scrip move freely, or under a light gate, everywhere else. That supports a globally tradable AMM market in a private company's equity without KYC on every counterparty, while every scrip holder who becomes a holder of record passes the full check. Where even trading must be gated, a whitelist transfer hook restricts every scrip transfer, mints included, to whitelisted addresses. Both models use the same contracts, and [Composability and DeFi](/how-cybercorps-works/composability) compares them as LiquiLeX pools.

The register keeps its own gate as well. A v5 LET contract consults its transfer hooks at two points, when a LET moves between addresses (possession) and when the holder of record changes (registration), and has a separate stop-transfer switch for each. See [Hooks](/building-on-the-protocol/reference/hooks).

## Credentials

### LeXcheX

LeXcheX is the protocol's accreditation and KYC/AML credential layer (see [`LexChex`](/building-on-the-protocol/reference/contracts/lexchex)). A credential is a soulbound NFT minted to a wallet once the holder completes onboarding: a questionnaire, a portfolio valuation and a countersigned agreement. `LexChexCondition` (`hasValidLexCheX`) checks it anywhere in the protocol. [LeXcheX](/using-the-apps/lexchex) describes the onboarding app, including accreditation from onchain holdings.

The `LeXcheXBadge` registry keeps every kind of credential in one soulbound contract: KYC/AML; accredited-investor, qualified-purchaser and QIB status; non-US status; investor jurisdiction and US state of residence; beneficial-owner counts for look-through accounting; and per-issuer whitelist and syndicate entitlements. A badge credential never changes after it is minted. A changed fact gets a newer credential, and revoking a credential only voids it, so every credential stays onchain for audit. Every read returns the credential's expiry with its value. One deployment can serve several credentialing operators: the registry's admins grant issuing authority per fact key (`setIssuerKeys`), each credential records the issuer that minted it, and a reader can accept only the issuers it trusts. The secondary-trading conditions read this registry.

### zkPassport

`NonUSNationalityCondition` checks a zkPassport proof that the address belongs to a non-US person, for Reg S and sanctions screening. The proof is zero-knowledge, so the address shows its nationality status without revealing who holds it, which suits the swap layer of an open LiquiLeX pool.

## Reg D and Reg S call for different gates

| Regime         | Typical pattern                                                                                                                         |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Reg D (US)     | LeXcheX accreditation and KYC on every primary investor and at conversion into a LET. Whitelisted LiquiLeX pool.                        |
| Reg S (non-US) | zkPassport non-US proof at primary issuance, an optional light zkPassport gate at swap, and a full credential at conversion into a LET. |
| Both           | An `OrCondition` accepting either path, so one company serves both audiences with two issuance flows.                                   |

## Holder caps

On a v5 company, `CyberScrip` can enforce a hard cap on the number of scrip holders (`maxHolderCount`), checked on every transfer and monitored through an onchain `holderCount`, and the company's admins set the cap directly on the scrip. On a v4 company only the IssuanceManager can call the setter, and the v4 IssuanceManager has no function that does, so the cap cannot be set.

On the register side, `HolderCapCondition` gates secondary trades against Investment Company Act §3(c)(1) and §3(c)(7) limits. It counts credentialed beneficial owners on a look-through basis instead of wallets, and counts an acquirer with no attestation as a US investor. The LET contracts' holder-count views can feed monitoring of the Exchange Act 12(g) threshold and its non-US analogues. The cyberCORPs app lists LET and scrip holders in the [Tokenization Hub](/using-the-apps/company/tokenization-hub) and shows a stakeholder count on the capTable page, but it does not track 12(g) thresholds.

## See also

* [Gate state transitions with conditions](/building-on-the-protocol/how-to/gate-with-conditions)
* [Restrict scrip transfers](/building-on-the-protocol/how-to/restrict-transfers)
* [Regulatory context](/how-cybercorps-works/regulatory-context)


# Upgrades and what MetaLeX controls

What MetaLeX can and cannot do to a cyberCORP: published implementations, opt-in upgrades, fees, templates, shared contracts, and the owner role the deploying factory keeps.

A platform that held admin keys over every issuer's records would be acting as each issuer's transfer agent. It would take on the registration and intermediary liability that come with that role, and the issuers' control of their own companies would be nominal. cyberCORPs is designed so that MetaLeX develops and maintains the protocol without holding authority inside a company's contracts. The exceptions are the deploying factory's owner role, the oldest companies' beacons and the contracts all companies share, and this page states each one.

## An upgrade needs a MetaLeX release and the company's opt-in

Each company's `CyberCorp`, `IssuanceManager`, `DealManager` and `RoundManager` sit behind their own UUPS proxies. Every one of them authorizes `upgradeToAndCall` only when two checks pass. The caller must hold `OWNER_ROLE` on the company's BorgAuth (the function is `onlyOwner`), and the new implementation must equal the reference implementation that the contract's recorded factory publishes (`getRefImplementation()`). Anything else reverts `NotRefImplementation`.

MetaLeX publishing an implementation therefore upgrades no company, and a company can stay on its implementation indefinitely. A company also cannot move to an implementation MetaLeX has not published, so it cannot escape the protocol's invariants by upgrading to a fork, and because the check is an exact match, the only target is the factory's current release. A company's LET contracts and scrip run as beacon proxies on two beacons that its own IssuanceManager owns. The owner moves each beacon with one call (`upgradeCertPrinterBeaconImplementation`, `upgradeScripBeaconImplementation`), which moves every LET contract, or every scrip contract, of the company at once, and each call is held to the factory's published reference in the same way.

Because each company opts in, versions coexist on the same chain. On Ethereum, Base and Arbitrum the factories publish v5 reference implementations, so a new company starts on v5, and an existing company keeps its deployed version until its owners upgrade it. Integrations read `DEPLOY_VERSION` on each contract instead of assuming one version per chain. A company upgrades its six contracts together, since mixed versions break deal, round and issuance calls. [Upgrade a cyberCORP](/building-on-the-protocol/how-to/upgrade-a-cybercorp) gives the steps, and [Run your company](/using-the-apps/company) covers the upgrade in the app.

The oldest companies run on beacon proxies whose beacons MetaLeX owns. Their owners cannot upgrade them, and MetaLeX can move those beacons for all such companies at once.

## The deploying factory keeps OWNER\_ROLE

Every address at level 99 or above on a company's BorgAuth holds `OWNER_ROLE`. That includes the officers (level 200), the company's own manager contracts, and the factory that deployed the company: `CyberCorpFactory` for most companies, with `PumpCorpFactory`, `MetaDAOFactory` and `ParentCoFactory` deploying the same way. BorgAuth gives its deployer `OWNER_ROLE`, and the factory code does not give it up.

An owner can change any address's role on BorgAuth and call every owner-gated function in the company's contracts, upgrades included. The current factory code never uses the role after the deployment transaction, but the factories are upgradeable by MetaLeX. While a factory holds the role, the guarantee that MetaLeX cannot push an upgrade, or change the company's roles, rests on MetaLeX not upgrading that factory to code that would. Anyone can check a company by reading `BorgAuth.userRoles(<factory address>)`, and an owner can remove the role with `updateRole(<factory address>, 0)`. [Access control](/building-on-the-protocol/reference/access-control) lists the roles the protocol assigns.

## MetaLeX publishes releases, sets fees and owns the shared contracts

MetaLeX develops the contracts and publishes new implementations to the factories. It sets the platform fees on the factories: a primary rate for rounds and deals, a separate rate for secondary trades, and per-company overrides of either. It maintains a library of agreement templates, but template creation on the `CyberAgreementRegistry` is permissionless, so anyone can register a template, and a standalone agreement creates its own template when it is made. MetaLeX operates LeXcheX as an oracle, which means MetaLeX or a delegate signs accreditation attestations into the credential registry.

MetaLeX also owns the contracts every company shares, among them the factories, the agreement registry, the `CertificateUriBuilder` that renders each LET's metadata and certificate image, and the certificate extensions. An upgrade to one of them reaches every company that uses it, with no opt-in. A company's owner can point its IssuanceManager at a different URI builder with `setUriBuilder`.

The reference apps (the cyberCORPs app, cyberRAISE, ACE, cyberSign and LeXcheX onboarding) are MetaLeX's as well. Nothing in the contracts depends on them or on the services behind them, and any front end can call the contracts directly ([Integrate from a frontend](/building-on-the-protocol/how-to/integrate-from-frontend)). A front end that helps investors find issuers may fall under the SEC staff's April 2026 statement on covered user interface providers, whoever runs it. [Regulatory context](/how-cybercorps-works/regulatory-context) has the details.

## Funds, approvals and the register stay with the company

MetaLeX holds no issuer funds. Money in flight sits in the LeXscroW escrow logic built into each company's own DealManager and RoundManager, and it moves only along the deal's signature, finalization and expiry paths. Deals are proposed and approved by the company's own BorgAuth owner and its signing officers, with no MetaLeX approval step. The company's BorgAuth roles belong to its officers, its governance addresses (board or officer multisigs) and its own contracts, and no MetaLeX wallet holds one. The only MetaLeX-controlled entry is the deploying factory described above.

MetaLeX keeps no register that overrides or competes with the chain. For tokenized units, the register of holders is the company's Ledger Entry Token (LET) contracts onchain. The cyberCORPs app's [cap table](/using-the-apps/company/captable) is the company's own working record. It mirrors the LETs and can also hold untokenized positions recorded offchain. The company's officers keep it in the app, and it exports to CSV, Excel and OCF. Which record is the company's legally definitive securities ledger depends on its governing documents. MetaLeX-form bylaws designate the onchain LETs for tokenized shares (see [Constitutive vs. pointer tokenization](/how-cybercorps-works/constitutive-vs-pointer)).

## See also

* [Upgrade model](/building-on-the-protocol/reference/upgrade-model)
* [Factories](/building-on-the-protocol/reference/factories)


# Composability and DeFi

What scrip can do in AMMs, lending markets and vesting contracts, and what its transfer restrictions and compliance powers cost in integrations.

A Ledger Entry Token (LET) names its holder and carries restriction legends, so DeFi protocols built for interchangeable ERC-20 units cannot use it. Scrip is the form they can use.

## Scrip can sit in pools, lending markets and vesting contracts

A LiquiLeX pool on Uniswap v4 quotes scrip against USDC continuously, and its `MetalexIssuerFeeHook` splits swap fees between the issuer and MetaLeX, so the company earns a share of every swap in its own scrip. Like USDC, scrip is an ERC-20 with optional compliance powers, so lending protocols that accept such tokens can list it as collateral. A standard ERC-20 vesting or streaming contract handles scrip as long as the scrip's transfer hooks admit it, and buybacks and other payments to holders can be made in scrip or settled against it. What a scrip holder is owed on the shares behind the scrip, dividends included, depends on the scrip's terms (see [Ledger Entry Tokens and scrip](/how-cybercorps-works/lets-and-scrip)).

## Each compliance choice narrows where scrip can go

### A transfer hook limits the venues

Scrip with a `WhitelistTransferHook` moves only between whitelisted addresses, and many integrations, permissionless ones especially, reject it. A company chooses between an open model (no transfer hook, compliance at conversion into a LET) and accepting that its scrip flows only through whitelisted venues.

### Compliance powers show even when unused

Force transfer, force burn and per-account freeze are opt-in powers chosen when the scrip is deployed, and the company's admins exercise them. Their presence is visible onchain whether or not anyone uses them, and some lending protocols decline to list any ERC-20 that has them. The scrip's one-way switches (`disableForceTransfer`, `disableForceBurn`, `disableFreeze`) let a company commit, irreversibly and verifiably, to an open posture.

### Registered ownership lives on the LET

A LET's holder of record is the registered owner of its units, and holding scrip registers no one. An integration that needs registered ownership, for voting, dividends to record holders or a DGCL §219 stockholder list, has to read the LET layer.

## Whitelisted and open LiquiLeX pools

|                                  | Whitelisted pool                                      | Open pool                               |
| -------------------------------- | ----------------------------------------------------- | --------------------------------------- |
| Check on each swap               | Transfer-hook whitelist (credentialed addresses only) | Optional zkPassport (sanctions, Reg S)  |
| Who can hold LP positions        | Whitelisted addresses only                            | Anyone                                  |
| Effect of trades on the register | None                                                  | None                                    |
| Gate at conversion into a LET    | Standard                                              | Standard, and the main compliance point |
| Suited to                        | High-touch private credit and Reg D issuers           | Reg S issuances and widely traded scrip |

## See also

* [Deploy a LiquiLeX pool](/building-on-the-protocol/how-to/deploy-liquilex-pool)
* [Compliance architecture](/how-cybercorps-works/compliance-architecture)
* [Hooks](/building-on-the-protocol/reference/hooks)


# Regulatory context

The US exemptions and SEC staff statements the protocol is built around, and what lies outside it. Not legal advice.

This page maps, briefly and incompletely, the regulatory backdrop the protocol is designed for. It is **not legal advice**. Issuers must consult their own counsel.

## Reg D private placements

Reg D is the standard exemption for unregistered private placements to US investors, with accreditation requirements under Rule 506(b) and 506(c) and limits on general solicitation. A company running a Reg D offering on the protocol checks LeXcheX accreditation credentials with `LexChexCondition` (`hasValidLexCheX`) at issuance, at deal close and when scrip converts into a Ledger Entry Token (LET). It signs from Reg D agreement templates such as `MetaLeX cyberSAFE US style Reg D`. For holder limits it can gate secondary trades with `HolderCapCondition`, which counts holders on a look-through basis, watch the onchain holder-count views for the 12(g) threshold, and, on a v5 company, cap scrip holders on the scrip itself (see [Restrict scrip transfers](/building-on-the-protocol/how-to/restrict-transfers)).

## Reg S offerings

Reg S covers offers and sales made outside the United States. The protocol supports it through `NonUSNationalityCondition`, which checks a zkPassport proof of non-US nationality, through Reg S agreement templates, and through LiquiLeX pools run either whitelisted or open with the matching gate.

## Secondary resales

On a v5 company, a secondary trade settles under an exemption pathway the buyer elects on accepting the offer: Rule 144, §4(a)(7), §4(a)(1½), Rule 144A or Reg S. Each pathway has its own conditions for holding periods, disclosure, distribution compliance, buyer eligibility and jurisdictional screens. Issuers enable only the pathways they support, and an unconfigured pathway blocks trades. [Compliance architecture](/how-cybercorps-works/compliance-architecture) describes the condition sets.

## SEC staff statements

In January 2026 the SEC's Division of Corporation Finance issued a joint staff statement confirming that the agency's existing framework for analyzing securities applies to tokenized securities ([statement](https://www.sec.gov/newsroom/speeches-statements/corp-fin-statement-tokenized-securities-012826)). The protocol is built for that framework, in which a security recorded onchain is still a security.

In April 2026 SEC staff issued guidance on covered user interface providers (File No. 4-894), creating a safe harbor for front-end operators that meet its conditions. It bears on anyone operating a front end that helps investors find issuers or trade, such as cyberRAISE, cyberTRADE, LiquiLeX or ACE front ends. The contracts behave the same whichever safe harbor a front-end operator relies on.

## Corporate law

The protocol depends on corporate and entity law for the designation that makes the onchain register the legal record. The Delaware General Corporation Law is the most fully worked-out case. Delaware LLC law, Cayman corporate and SPC regimes, BVI corporate law, English company law (Companies Act 2006) and partnership and fund statutes also accommodate the designation. [Legal mappings](/how-cybercorps-works/legal-mappings) sets out the provisions.

## Outside the protocol's scope

Whether a particular front end needs broker-dealer registration is a question for its operator, and the April 2026 staff statement sets out the conditions of its safe harbor. AIFMD, MiCA and national private-placement regimes are not encoded in the contracts, so issuers subject to them configure conditions and agreements to comply. Tax treatment is for the issuer and the holder to work out from the events the protocol records.

## See also

* [MetaLeX on Substack](https://metalex.substack.com/), for commentary on regulatory developments.


# Guides

What developers can build on the cyberCORPs contracts, which contracts each MetaLeX product uses, and the order to read the guides in

These guides call the cyberCORPs contracts directly. With them you can form a company onchain and issue its securities as Ledger Entry Tokens (LETs), raise money into it, execute agreements through the agreement registry, settle secondary trades on LETs or in scrip, list scrip in an AMM pool, and deploy specialized structures such as an ACE offering or a MetaDAO SPC.

MetaLeX's apps (see [Using the apps](/using-the-apps/webapp)) are one front end on these contracts. A script, another dApp or a back-office system can make the same calls, and the contracts do not depend on any MetaLeX app or its backend services.

The code samples use the real signatures from [`cybercorps-contracts`](https://github.com/MetaLex-Tech/cybercorps-contracts) (`develop`) and show the order of calls. Several structs are long, so check every field against the source before you deploy.

## What you need

* [Foundry](https://book.getfoundry.sh/) and a clone of `cybercorps-contracts` on which `forge build` succeeds (the default profile compiles with `via_ir`).
* An RPC endpoint for Base Sepolia and a funded test wallet there, or a local fork of Base Sepolia. None of the guides needs live funds.
* Enough Solidity to read a contract.

## Reading order

Start with the two end-to-end guides. They build the company and the LETs that the task guides start from.

1. [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp) deploys a company and its contract suite, creates a LET contract for its common stock, and issues the founder's first LET.
2. [Run a cyberRAISE round](/building-on-the-protocol/how-to/run-a-cyberraise-round) creates a SAFE round, takes an investor's Expression of Interest, allocates it and closes the round.

Then pick the task you need.

**Issue and control securities**

* [Issue a LET](/building-on-the-protocol/how-to/issue-a-let)
* [Scripify and settle a secondary trade](/building-on-the-protocol/how-to/scripify-and-settle)
* [Restrict scrip and LET transfers](/building-on-the-protocol/how-to/restrict-transfers)
* [Configure BorgAuth roles](/building-on-the-protocol/how-to/configure-roles)
* [Gate state transitions with conditions](/building-on-the-protocol/how-to/gate-with-conditions)

**Sign agreements and settle trades**

* [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement)
* [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade)
* [Convert SAFEs to equity](/building-on-the-protocol/how-to/convert-safe-to-equity)

**Deploy specialized structures**

* [Deploy a PumpCorp for ACE](/building-on-the-protocol/how-to/deploy-pumpcorp-ace)
* [Deploy a MetaDAO SPC](/building-on-the-protocol/how-to/deploy-metadao-spc)
* [Deploy a LiquiLeX pool](/building-on-the-protocol/how-to/deploy-liquilex-pool)

**Operate and integrate**

* [Upgrade a cyberCORP](/building-on-the-protocol/how-to/upgrade-a-cybercorp)
* [Integrate from a frontend](/building-on-the-protocol/how-to/integrate-from-frontend)

Function-level detail is in the [Reference](/building-on-the-protocol/reference), and the design behind it in [How cyberCORPs works](/how-cybercorps-works/explanation).

## Which contracts each product uses

| Product        | What it does onchain                                                                            | Contracts                                                                                                                                                                                                                                                                                                              | Guides                                                                                                                                                                                                                                                                                                                             |
| -------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cyberCORPs app | Forms and administers a company: officers, LET contracts, issuance, scrip and transfer controls | `CyberCorpFactory`, `CyberCorp`, `BorgAuth`, [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager), [`LedgerEntryToken`](/building-on-the-protocol/reference/contracts/ledgerentrytoken), [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip)                        | [Incorporate](/building-on-the-protocol/how-to/incorporate-a-cybercorp), [Issue a LET](/building-on-the-protocol/how-to/issue-a-let), [Roles](/building-on-the-protocol/how-to/configure-roles), [Transfers](/building-on-the-protocol/how-to/restrict-transfers), [Upgrade](/building-on-the-protocol/how-to/upgrade-a-cybercorp) |
| cyberRAISE     | Primary fundraising rounds                                                                      | [`RoundManager`](/building-on-the-protocol/reference/contracts/roundmanager), [`DealManager`](/building-on-the-protocol/reference/contracts/dealmanager), [`LeXscroWLite`](/building-on-the-protocol/reference/contracts/lexscrowlite)                                                                                 | [Run a cyberRAISE round](/building-on-the-protocol/how-to/run-a-cyberraise-round)                                                                                                                                                                                                                                                  |
| cyberTRADE     | Settlement of negotiated secondary trades                                                       | [`DealManager`](/building-on-the-protocol/reference/contracts/dealmanager), [`LeXscroWLite`](/building-on-the-protocol/reference/contracts/lexscrowlite), [`IssuanceManager`](/building-on-the-protocol/reference/contracts/issuancemanager), [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip) | [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade), [Scripify and settle](/building-on-the-protocol/how-to/scripify-and-settle)                                                                                                                                                                       |
| cyberSign      | Agreement templates and multi-party execution                                                   | [`CyberAgreementRegistry`](/building-on-the-protocol/reference/contracts/cyberagreementregistry)                                                                                                                                                                                                                       | [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement)                                                                                                                                                                                                                                                    |
| ACE            | Token-community conversion into equity                                                          | [`PumpCorpFactory`](/building-on-the-protocol/reference/factories), [`ACESAFEExtension`](/building-on-the-protocol/reference/extensions)                                                                                                                                                                               | [Deploy a PumpCorp for ACE](/building-on-the-protocol/how-to/deploy-pumpcorp-ace)                                                                                                                                                                                                                                                  |
| LiquiLeX       | AMM liquidity for scrip                                                                         | [`MetalexIssuerFeeHook`](/building-on-the-protocol/reference/hooks) on Uniswap v4, [`CyberScrip`](/building-on-the-protocol/reference/contracts/cyberscrip)                                                                                                                                                            | [Deploy a LiquiLeX pool](/building-on-the-protocol/how-to/deploy-liquilex-pool)                                                                                                                                                                                                                                                    |
| MetaDAO        | Futarchy-governed Cayman SPC                                                                    | [`MetaDAOFactory`](/building-on-the-protocol/reference/factories)                                                                                                                                                                                                                                                      | [Deploy a MetaDAO SPC](/building-on-the-protocol/how-to/deploy-metadao-spc)                                                                                                                                                                                                                                                        |
| LeXcheX        | Soulbound accreditation and KYC/AML credentials                                                 | [`LexChex`, `LeXcheXBadge`, `LexChexMinter`](/building-on-the-protocol/reference/contracts/lexchex)                                                                                                                                                                                                                    | [Gate state transitions with conditions](/building-on-the-protocol/how-to/gate-with-conditions)                                                                                                                                                                                                                                    |

### cyberRAISE

Issuers configure rounds with raise caps, ticket sizes, pricing, payment tokens (typically USDC), security types (SAFE, SAFT, SAFTE and equity rounds) and a round mode: first-come or founder-approved. Investors submit EIP-712-signed Expressions of Interest, the round and deal managers hold their payments in escrow, and allocation mints the investors' LETs.

### cyberTRADE

cyberTRADE settles secondary trades of fund interests, LLC membership interests, LP units, private company stock and other private securities, in US and non-US jurisdictions. The parties find each other and negotiate elsewhere. cyberTRADE verifies compliance, executes the agreement, holds escrow and settles atomically, along one of two paths:

* **LET path.** The DealManager's offer flow. At settlement the seller's LET is reduced or used up, and a new LET carrying the seller's endorsement is minted to the buyer. The buyer elects an exemption pathway (Rule 144, §4(a)(7), §4(a)(1½), Rule 144A or Regulation S), and the trade must pass that pathway's conditions plus the issuer's own: holder caps, accreditation, qualified-purchaser status, holding periods, jurisdiction screens and, where the issuer installs one, a per-deal approval condition. Settlements pay the DealManager's secondary fee, which is set separately from the primary fee on rounds and deals.
* **Scrip path.** Settlement in scrip, with conversion back into a LET deferred until the holder asks for it. LiquiLeX pools run on this path.

### cyberSign

Anyone can register a template in the `CyberAgreementRegistry`, and a standalone agreement creates its template in the same transaction. Parties countersign onchain with EIP-712 signatures, and executed agreements are linked to LETs and deal records. cyberSign works as a signing layer for any legal instrument, with or without a raise.

### LiquiLeX

LiquiLeX pairs a company's scrip against a stablecoin in a Uniswap v4 pool whose `MetalexIssuerFeeHook` routes swap fees to MetaLeX and the issuer. A whitelisted pool lets only whitelisted addresses hold or move the scrip. An open pool enforces compliance when scrip converts back into a LET, optionally with a zkPassport check at the swap.

### LeXcheX

LeXcheX issues soulbound credentials: the original accreditation NFT and the `LeXcheXBadge` registry, which covers accreditation, qualified-purchaser and QIB status, jurisdiction and beneficial-owner attestations, and per-issuer whitelists. Conditions read these credentials to gate rounds and trades.

## Where the reference front ends live

MetaLeX's reference front ends are in the [`metalex-webapp`](https://github.com/MetaLex-Tech/metalex-webapp) monorepo:

| Product        | Route                                                              | Source                                                                                                                                                                                                                                  |
| -------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cyberCORPs app | `/cybercorps`                                                      | [`apps/cybercorps-web/src/app/(frame-layout)/cybercorps`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/cybercorps-web/src/app/%28frame-layout%29/cybercorps)                                                        |
| cyberRAISE     | `/cyberraise`                                                      | [`apps/cybercorps-web/src/app/(frame-layout)/cyberraise`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/cybercorps-web/src/app/%28frame-layout%29/cyberraise)                                                        |
| ACE            | `/ace`, with `/ace/bridge-to-solana` and `/ace/bridge-from-solana` | [`apps/cybercorps-web/src/app/ace`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/cybercorps-web/src/app/ace)                                                                                                        |
| cyberSign      | `/cybersign`                                                       | [`apps/web/src/app/(frame-layout)/cybersign`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/web/src/app/%28frame-layout%29/cybersign)                                                                                |
| MetaDAO        | `/metadao`                                                         | [`apps/cybercorps-web/src/app/(frame-layout)/metadao`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/cybercorps-web/src/app/%28frame-layout%29/metadao)                                                              |
| LeXcheX        | lexchex.metalex.tech                                               | [`apps/lexchex-web`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/lexchex-web), with the oracle service in [`apps/lexchex-oracle`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/lexchex-oracle) |

The cyberCORPs app's **cyberSign** sidebar entry opens cyberSign. The app works with v4 and v5 companies side by side and chooses each call's shape from the targeted contract's `DEPLOY_VERSION`; [Integrate from a frontend](/building-on-the-protocol/how-to/integrate-from-frontend) shows how to do the same.


# Incorporate a cyberCORP

Deploy a cyberCORP and its contract suite, create a LET contract for its common stock, and issue the founder's first Ledger Entry Token

This guide deploys a new cyberCORP through a v5 factory, creates the Ledger Entry Token (LET) contract for its Common Stock, and issues the first LET to a founder. You finish with a `CyberCorp` and its suite (`IssuanceManager`, `DealManager`, `RoundManager` and a `BorgAuth` ACL), one LET contract for Common Stock, and one LET held by the founder.

## 1. Deploy the company and its suite

`CyberCorpFactory.deployCyberCorp` deploys the BorgAuth ACL and every contract in the suite in one call.

```solidity
import {CompanyOfficer} from "src/CyberCorpConstants.sol";

CyberCorpFactory factory = CyberCorpFactory(FACTORY_ADDR);

CompanyOfficer memory officer = CompanyOfficer({
    eoa:     founder,
    name:    "Jane Founder",
    contact: "jane@acme.example",
    title:   "Chief Executive Officer"
});

(
    address cyberCorp,
    address auth,
    address issuanceManager,
    address dealManager,
    address roundManager
) = factory.deployCyberCorp(
    keccak256("acme-cyberco-v1"),     // salt (must be non-zero)
    "Acme CyberCo, Inc.",            // companyName
    "corporation",                   // companyType (free-form text)
    "Delaware",                      // companyJurisdiction
    "legal@acme.example",            // companyContactDetails
    "Delaware Court of Chancery",     // defaultDisputeResolution
    founder,                          // companyPayable
    officer
);
```

The factory grants BorgAuth level `200` (officer) to the founder and to the `CyberCorp`, and level `99` (owner) to the IssuanceManager, DealManager and RoundManager. The factory also keeps the level `99` that BorgAuth gives the address that deploys it; see [Access control](/building-on-the-protocol/reference/access-control). The call emits `CyberCorpDeployed`.

Before deploying, the factory hashes the salt with the company name, type, jurisdiction, contact details, dispute forum, payout address and officer (`computeDeploymentSalt`), and each component factory then namespaces the result by its caller. Change any of those arguments and the addresses change, so a deployment with other terms cannot take the addresses your configuration predicts. Anyone may submit your exact configuration, and the result is the same company with your officer in control. On a v5 factory, every component of the new company reports `DEPLOY_VERSION` `"5"`.

## 2. Create a LET contract for Common Stock

The `IssuanceManager` creates one LET contract (a `LedgerEntryToken` deployment) per security series.

```solidity
import {SecurityClass, SecuritySeries} from "src/CyberCorpConstants.sol";

string[] memory legend = new string[](1);
legend[0] = "These securities have not been registered under the Securities Act of 1933...";

address commonPrinter = IIssuanceManager(issuanceManager).createCertPrinter(
    legend,                       // default legend
    "Acme CyberCo Common Stock",  // name
    "ACME-CS",                    // ticker
    "ipfs://acme-cert-art",       // certificate URI
    SecurityClass.CommonStock,
    SecuritySeries.NA,
    SHARE_EXTENSION_ADDR,         // certificate extension for this series
    ""                            // seriesData, the extension-encoded series-scope
                                  // payload; empty when the extension has none
);
```

## 3. Issue the founder's first LET

`createCertAndAssign` mints the founder's LET. Its metadata is a `CertificateDetails` struct, defined in [`ILedgerEntryToken.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILedgerEntryToken.sol).

```solidity
import {CertificateDetails} from "src/interfaces/ILedgerEntryToken.sol";

CertificateDetails memory details = CertificateDetails({
    signingOfficerName:                  "Jane Founder",
    signingOfficerTitle:                 "Chief Executive Officer",
    investmentAmountUSD:                 0,
    issuerUSDValuationAtTimeOfInvestment: 0,
    unitsRepresented:                    8_000_000e18, // 8,000,000 shares (18-decimal)
    legalDetails:                        "Founder common stock",
    extensionData:                       ""   // ABI-encoded per the extension
});

uint256 tokenId = IIssuanceManager(issuanceManager).createCertAndAssign(
    commonPrinter,
    founder,
    details
);
```

`createCertAndAssign` mints the ERC-721 and records the founder as its registered owner. On a v5 LET contract, `createCert` also records the recipient as registered owner, with no name. The other `createCert*` variants attach a name, an endorsement or a signature; [Issue a LET](/building-on-the-protocol/how-to/issue-a-let) compares them.

## 4. Read the LET back

```solidity
LedgerEntryToken printer = LedgerEntryToken(commonPrinter);

string  memory uri        = printer.tokenURI(tokenId);     // onchain JSON + SVG
address         tokenHolder = printer.ownerOf(tokenId);     // ERC-721 holder
address         registered  = printer.legalOwnerOf(tokenId);// registered owner of record
```

A LET tracks two owners. `ownerOf` returns the wallet that holds the token, and `legalOwnerOf` returns the registered owner of record. They can differ: an administered lot sits in an admin multisig while the buyer is its registered owner. [LETs and scrip](/how-cybercorps-works/lets-and-scrip) explains the model.

Next, [run a cyberRAISE round](/building-on-the-protocol/how-to/run-a-cyberraise-round) on the new company. Function-level detail is in [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager) and [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken).


# Run a cyberRAISE round

Create a SAFE round on a cyberCORP, take an investor's Expression of Interest, allocate it and close the round

This guide creates a SAFE round on a cyberCORP's `RoundManager`, takes an investor's Expression of Interest (EOI), allocates it and closes the round. Allocation issues the investor's SAFE as a Ledger Entry Token (LET). The `Round` and `EOI` structs are large, and the snippets show only the fields this flow sets, so check the source for every field.

```mermaid
flowchart TD
    A["Officer builds the Round<br/>draft · setTickets · setAgreement"] --> B["createRound<br/>(officer's escrowed EIP-712 signature)"]
    B --> C["Round live"]
    C --> D["Investor submits EOI<br/>payment escrowed (not yet gated)"]
    D --> E{Round type}
    E -- "FCFS, automatic in<br/>the same transaction" --> G["Allocation<br/>conditions checked here · LET issued ·<br/>agreement executed with the escrowed officer signature ·<br/>escrow finalized and payment released to the issuer"]
    E -- "FounderApproved,<br/>officer calls allocate" --> G
    D -. "officer rejects<br/>(any time before allocation)" .-> R["Refund to investor"]
    D -. "investor recalls<br/>(after EOI expiry or round end)" .-> R
    G --> I["Round closes<br/>closeRoundNow or endTime"]
```

You need a cyberCORP from [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp): its `roundManager` address and an officer key.

## 1. Build the round

A round is a `Round` struct ([`RoundLib.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/libs/RoundLib.sol)). Build it by chaining the `RoundLib` builder's `draft()`, `setTickets` and `setAgreement`.

```solidity
import {RoundLib, Round, RoundType} from "src/libs/RoundLib.sol";
import {SecuritySeries} from "src/CyberCorpConstants.sol";
using RoundLib for Round;

Round memory round = RoundLib.draft()
    .setTickets(
        SecuritySeries.NA,        // seriesType
        RoundType.FounderApproved,// FCFS or FounderApproved
        false,                    // publicRound
        true,                     // allowTimedOffers
        false,                    // restrictEndTimeReduction
        2_000_000e18,             // raiseCap   (USD, 18 decimals)
        25_000e18,                // minTicket
        500_000e18,               // maxTicket
        USDC,                     // paymentToken
        1e18,                     // pricePerUnit (USD, 18 decimals)
        20_000_000e18,            // valuation
        block.timestamp,          // startTime
        block.timestamp + 30 days // endTime
    )
    .setAgreement(
        TEMPLATE_ID,              // agreement template id
        officer,                  // authorityOfficer
        "Jane Founder",           // officerName
        "Chief Executive Officer",// officerTitle
        legalDetails,             // string[] (one per LET contract)
        roundPartyValues,         // string[]
        extensionData,            // bytes[]  (one per LET contract)
        conditions,               // address[] of ICondition gates
        escrowedSignature         // bytes, the officer's escrowed signature
    );
```

`RoundType.FCFS` accepts EOIs first-come. `RoundType.FounderApproved` requires the officer to allocate each one.

The `escrowedSignature` is the `authorityOfficer`'s EIP-712 signature over the round's economic parameters (the `EscrowedSignatureData` struct in [`RoundManagerStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/storage/RoundManagerStorage.sol)), under the domain `"RoundManager"`, version `"1"`, with the RoundManager as verifying contract. `createRound` recomputes the hash from the draft and reverts `InvalidEscrowedSignature` if the signature does not verify.

## 2. Create the round

`createRound` also creates a LET contract for each `CyberCertData` entry you pass.

```solidity
import {CyberCertData, SecurityClass} from "src/CyberCorpConstants.sol";

CyberCertData[] memory certData = new CyberCertData[](1);
certData[0] = CyberCertData({
    name:           "SAFE",
    symbol:         "ACME-SAFE",
    uri:            "ipfs://acme-safe-art",
    securityClass:  SecurityClass.SAFE,
    securitySeries: SecuritySeries.NA,
    extension:      SAFE_EXTENSION_ADDR,
    seriesData:     "",       // series-scope payload encoded by `extension`
    defaultLegend:  legend
});

bytes32 roundId = IRoundManager(roundManager).createRound(round, certData);
```

## 3. Submit the investor's EOI

The investor signs the round's agreement offchain and submits an `EOI` struct ([`RoundManagerStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/storage/RoundManagerStorage.sol)). The signature is the agreement registry's EIP-712 `SignatureData` for the EOI agreement that `submitEOI` creates (the round's template, the investor's `salt` and `secretHash`, and the RoundManager as finalizer), with the investor as `signer`. [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement) gives the typed data. The registry verifies the signature inside `submitEOI`, so a signature over the wrong id or type reverts the whole call.

```solidity
import {EOI} from "src/storage/RoundManagerStorage.sol";

EOI memory eoi = EOI({
    name:          "Alice Investor LLC",
    investorType:  "entity",
    jurisdiction:  "USA",
    contact:       "alice@example.com",
    minAmount:     100_000e6,    // in payment-token decimals
    maxAmount:     100_000e6,
    expiry:        block.timestamp + 14 days,
    naturalPerson: false,
    lexchexDetails: lexchexDetails    // see LexChexDetails in the source
});

(bytes32 agreementId, uint256 tokenId) = IRoundManager(roundManager).submitEOI(
    roundId,
    eoi,
    globalValues,     // string[]
    partyValues,      // string[]
    investorSignature,// bytes (EIP-712)
    salt,             // uint256
    eoiConditions,    // address[]
    secretHash        // bytes32
);
```

## 4. Allocate the EOI

In a founder-approved round the officer allocates each accepted EOI. An FCFS round allocates inside `submitEOI`, so it skips this step. `allocate` prices the ticket, mints a SAFE LET from each of the round's LET contracts, attaches the officer's escrowed signature and an endorsement, and refunds any rounding dust.

```solidity
uint256 certTokenId = IRoundManager(roundManager).allocate(
    agreementId,
    100_000e6        // allocatedAmount, in payment-token decimals
);
```

## 5. Close the round

`closeRoundNow` closes the round before its `endTime`. Without it, the round closes at `endTime`.

```solidity
IRoundManager(roundManager).closeRoundNow(roundId);
```

The investor now holds a SAFE LET. To trade it, [run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade) through the DealManager or [scripify it and settle in scrip](/building-on-the-protocol/how-to/scripify-and-settle). Function-level detail is in [RoundManager](/building-on-the-protocol/reference/contracts/roundmanager).


# Issue a LET

Create a LET contract for a security class and series and issue Ledger Entry Tokens from it

A Ledger Entry Token (LET) records one lot of a cyberCORP security: stock, a SAFE, an option or any other [supported security type](/building-on-the-protocol/reference/security-types). This guide issues LETs through the company's `IssuanceManager`.

You need the company's `issuanceManager` address and a LET contract (a `LedgerEntryToken` deployment) for the security's class and series. Step 1 creates one if the series has none.

## 1. Create the LET contract, if the series has none

```solidity
import {SecurityClass, SecuritySeries} from "src/CyberCorpConstants.sol";

address printer = IIssuanceManager(issuanceManager).createCertPrinter(
    defaultLegend,                 // string[]
    "Acme Series A Preferred",     // name
    "ACME-A",                      // ticker
    "ipfs://acme-cert-art",        // certificate URI
    SecurityClass.PreferredStock,
    SecuritySeries.SeriesA,
    SHARE_EXTENSION_ADDR,          // certificate extension
    ""                             // seriesData, the extension-encoded
                                   // series-scope payload; "" if none
);
```

The IssuanceManager files the new LET contract under its `SecurityClass`, creating an empty class if none exists. Its transfer switches all start closed: LETs issue freely, but holders cannot move them and the holder of record cannot change until an admin opens the contract with `setGlobalTransferable`, `setGlobalLegalTransferable` or the per-lot equivalents. [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) documents the delivery and registration gates.

## 2. Build the `CertificateDetails`

The struct is defined in [`ILedgerEntryToken.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILedgerEntryToken.sol).

```solidity
import {CertificateDetails} from "src/interfaces/ILedgerEntryToken.sol";

CertificateDetails memory details = CertificateDetails({
    signingOfficerName:                  "Jane Founder",
    signingOfficerTitle:                 "Chief Executive Officer",
    investmentAmountUSD:                 2_500_000e18,
    issuerUSDValuationAtTimeOfInvestment: 20_000_000e18,
    unitsRepresented:                    1_000_000e18, // 1,000,000 shares (18-decimal)
    legalDetails:                        "Series A Preferred",
    extensionData:                       abi.encode(/* per the extension */)
});
```

{% hint style="warning" %}
`unitsRepresented`, `investmentAmountUSD` and `issuerUSDValuationAtTimeOfInvestment` are all **18-decimal fixed point**: one share (or one dollar) is `1e18`.
{% endhint %}

## 3. Mint the LET

Every mint function registers a holder of record. They differ in what else they write.

| Function                                                                                                             | Use when                                                                                                                              |
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `createCert(certAddress, to, details)`                                                                               | Bare mint: `to` is registered with a blank name, and no endorsement or signature is written.                                          |
| `createCertAndAssign(certAddress, investor, details)`                                                                | Mint with an issuance endorsement naming the investor, dated now.                                                                     |
| `createCertAndAssignWithName(certAddress, investor, details, investorName, endorsementSignature, timestamp)`         | As above, plus the holder's legal name, an officer signature, and an endorsement date you choose (a reissue keeps the original date). |
| `createCertSignAndAssign(certAddress, investor, details, endorsementSignature, registry, agreementId, investorName)` | As above, but the endorsement points at an agreement in a registry and is dated now.                                                  |

```solidity
uint256 tokenId = IIssuanceManager(issuanceManager).createCertAndAssign(
    printer, investor, details
);
```

To correct the holder of record on an existing lot outside a trade, call `assignCert(certAddress, from, tokenId, investor, details, investorName)`, where `from` is the current holder of record. The LET contract's registration gate must be open.

## 4. Endorse, sign or void an existing LET

These operations live on the LET contract itself, and the IssuanceManager or a BorgAuth admin can call them:

* **Endorse:** `endorseCertificate(tokenId, endorser, signature, agreementId)` assembles the endorsement onchain. The registered owner can also call `addEndorsement(tokenId, endorsement)` directly.
* **Issuer signature:** `addIssuerSignature(tokenId, signature)`.
* **Void or unvoid:** `voidCert(tokenId)` and `unvoidCert(tokenId)`.

Function-level detail is in [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager) and [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken).


# Configure BorgAuth roles

Add and remove officers, set BorgAuth levels directly, and transfer or renounce ownership of a cyberCORP's ACL

A cyberCORP holds its authority in a BorgAuth ACL. Roles are numeric levels, and a check passes for any level at or above the one it requires. The full model is in [Access control](/building-on-the-protocol/reference/access-control).

## Role levels on a cyberCORP

| Level               | Meaning                                                                                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `99` (`OWNER_ROLE`) | Owner. Can grant and revoke roles and approve upgrades. Held by the suite's manager contracts and, from deployment, by the deploying factory.                                   |
| `98` (`ADMIN_ROLE`) | Admin. Gates operational functions such as scrip compliance actions, hook updates and opening a Ledger Entry Token (LET) contract's transfer switches. Any level `≥ 98` passes. |
| `200`               | Company officer. Set for an officer's address. Because `200 ≥ 99`, officers also pass `onlyOwner`.                                                                              |
| `0`                 | No authority.                                                                                                                                                                   |

## Manage officers through the CyberCorp

`CyberCorp.addOfficer` records an officer and grants their address level `200` in one call:

```solidity
import {CompanyOfficer} from "src/CyberCorpConstants.sol";

CyberCorp(cyberCorp).addOfficer(CompanyOfficer({
    eoa:     newOfficer,
    name:    "Sam Officer",
    contact: "sam@acme.example",
    title:   "Chief Financial Officer"
}));
```

Adding an address already listed as an officer reverts `DuplicateOfficer`. An address that already holds a level above `200` keeps it, so the grant never downgrades a custom role.

`updateOfficer` replaces the record at an index with a new title, contact or address. When the address changes, the old address loses its level-`200` grant and the new one receives it under the same rules as `addOfficer`:

```solidity
CyberCorp(cyberCorp).updateOfficer(2, CompanyOfficer({
    eoa:     replacementOfficer,
    name:    "Pat Officer",
    contact: "pat@acme.example",
    title:   "Chief Financial Officer"
}));
```

`removeOfficer` and `removeOfficerAt` each delete the officer record and revoke the level-`200` grant:

```solidity
CyberCorp(cyberCorp).removeOfficer(departedOfficer);
// or by index:
CyberCorp(cyberCorp).removeOfficerAt(2);
```

Removal zeroes a level only when it is exactly `200`, so a custom level granted directly on the BorgAuth survives the officer's removal. `addOfficer` refuses duplicates, but an existing company's officer list can still hold one address at several indexes. In that case the address keeps its role while any entry still lists it, and only removing or updating away its last entry revokes it.

## Set a level directly on the BorgAuth

`updateRole` on the BorgAuth contract sets any level. The caller must hold `OWNER_ROLE`.

```solidity
BorgAuth auth = BorgAuth(BorgAuthACL(cyberCorp).AUTH());
auth.updateRole(someAddress, 200);   // grant officer level
auth.updateRole(someAddress, 0);     // revoke
```

The factory that deployed the company starts with level `99` on its BorgAuth and does not give it up on its own. `auth.userRoles(factory)` shows whether your company still grants it. [Access control](/building-on-the-protocol/reference/access-control) lists every role the protocol assigns.

## Transfer ownership in two steps

```solidity
auth.initTransferOwnership(newOwner);   // by current owner
// then, as newOwner:
auth.acceptOwnership();
```

## Renounce your own level

`auth.zeroOwner()` sets the caller's level to `0` and permanently removes its admin control.


# Restrict scrip and LET transfers

Install transfer-restriction hooks on scrip and LETs, use or permanently disable scrip compliance powers, and cap scrip holders

Scrip is an ERC-20 (`CyberScrip`). You restrict it with transfer-restriction hooks, and the company can hold compliance powers over it and later give them up. Ledger Entry Tokens (LETs) have their own switches for delivery and registration.

## Install transfer-restriction hooks on scrip

Hooks implement `ITransferRestrictionHook`. CyberScrip runs every installed hook's `checkTransferRestriction` on each transfer and on each mint (any move to a nonzero address), so a scripify to a recipient the hooks reject fails. Burns skip the hooks. See [Hooks](/building-on-the-protocol/reference/hooks).

You set the hooks when you deploy the scrip (the `typeRestrictionHooks` argument of `deployCyberScrip`), and a BorgAuth admin can replace them later on the scrip itself:

```solidity
// at deploy time
address cyberScrip = IIssuanceManager(issuanceManager).deployCyberScrip(
    certAddress,
    typeRestrictionHooks,   // ITransferRestrictionHook[]
    /* ...remaining args... */
);

// later, as a BorgAuth admin; replaces the whole hook set
ICyberScrip(cyberScrip).setRestrictionHook(newHooks);
```

The repository's hook implementation is `WhitelistTransferHook` in [`src/hooks/transfer/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/hooks/transfer/README.md). It allows a transfer when both sender and recipient are whitelisted, and a mint when the recipient is. Its source lists its admin functions.

## Open and close a LET contract's gates

A LET contract (`LedgerEntryToken`) restricts two events separately. The IssuanceManager or a BorgAuth admin sets both on the LET contract:

* **Delivery** (the token moves between wallets): the contract-wide `setGlobalTransferable` and per-lot `setTokenTransferable` switches, plus each hook's `checkTransferRestriction`.
* **Registration** (the holder of record changes): the contract-wide `setGlobalLegalTransferable` and per-lot `setTokenLegalTransferable` switches, plus each hook's `checkLegalTransferRestriction`.

Install hooks with `setRestrictionHook(id, hook)` for one token and `setGlobalRestrictionHook(hook)` for the whole contract. All four switches start `false` on a new LET contract, so LETs issue freely but cannot move, and the holder of record cannot change, until an admin opens them. The gates are documented in [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken).

## Use the scrip compliance powers

A CyberScrip is deployed with up to three compliance powers, chosen by the last three booleans of `deployCyberScrip`:

```solidity
    /* ... */ true /*enableForceTransfer*/, true /*enableForceBurn*/, true /*enableFreeze*/
```

The scrip has no blocklist. Force transfer, force burn and freeze are the only powers.

| Power          | Exercised via                                                  |
| -------------- | -------------------------------------------------------------- |
| Force transfer | `CyberScrip.forceTransfer(from, to, amount)`                   |
| Force burn     | `IssuanceManager.forceScripBurn(certAddress, account, amount)` |
| Freeze         | `CyberScrip.setFrozen(account, isFrozen)`                      |

An admin calls force transfer and freeze on the scrip directly, since those functions are `onlyIssuanceManagerOrAdmin`. Force burn runs through the IssuanceManager because it also withdraws the matching backing units from the scrip pool. A frozen account cannot move scrip, scripify, or convert scrip back into a LET.

## Disable a power permanently

Each power has a one-way disable on CyberScrip (`disableForceTransfer()`, `disableForceBurn()`, `disableFreeze()`). A disabled power cannot be re-enabled, and exercising it reverts `ComplianceFeatureDisabled`. The disables are `onlyIssuanceManagerOrAdmin`, like the other controls.

## Cap the number of scrip holders

`CyberScrip.setMaxHolderCount(n)` caps the holder count (`0` means unlimited), and a transfer that would exceed the cap reverts `HolderLimitExceeded`. The cap is `onlyIssuanceManagerOrAdmin` and can be changed at any time.

Function-level detail is in [CyberScrip](/building-on-the-protocol/reference/contracts/cyberscrip) and [Hooks](/building-on-the-protocol/reference/hooks).


# Gate state transitions with conditions

Attach condition contracts to scripification, rounds, deals and secondary settlement, and write a custom condition

A condition is a contract implementing `ICondition`. Attaching conditions lets a company require any onchain check before scrip is minted or redeemed, an investor joins a round, a deal proceeds or a secondary trade settles.

```solidity
interface ICondition {
    function checkCondition(
        address _contract,
        bytes4 _functionSignature,
        bytes memory data
    ) external view returns (bool);
}
```

[Conditions](/building-on-the-protocol/reference/conditions) documents the built-in conditions (`lexchexCondition`, `NonUSNationalityCondition`, `IssuerApprovalRecertificationCondition`, `OrCondition`). The secondary-trading conditions live in [`src/libs/conditions/secondary/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/libs/conditions/secondary/README.md) and cover eligibility, holding periods, holder caps, Reg S distribution compliance, Rule 144 and Section 4(a)(7) disclosure, CFIUS, a kill switch and more.

## Attach conditions where the protocol accepts them

Pass conditions as an `address[]` (or `ICondition[]`) to the call that creates the gated thing. Secondary trades are the exception: the DealManager keeps their conditions as standing lists.

### Scripification and de-scripification

Set both lists when you deploy the scrip:

```solidity
IIssuanceManager(issuanceManager).deployCyberScrip(
    certAddress,
    typeRestrictionHooks,
    certToScripConditions,   // ICondition[] gating scripifyCert
    scripToCertConditions,   // ICondition[] gating convertScripToCert
    /* ...remaining args... */
);
```

### Fundraising rounds

Round-wide conditions go in the `Round` built with `RoundLib.setAgreement` (`roundConditions`). Per-EOI conditions go in `submitEOI(..., conditions, ...)`.

### Deals

Pass them as the `conditions` argument of `DealManager.proposeDeal`.

### Secondary trades

The company sets these lists once on the DealManager, and the DealManager evaluates them at post, accept and finalize:

```solidity
// exemption-pathway conditions (also enables/disables the pathway)
dealManager.setPathwayThresholdConditions(pathway, conditions, enabled);
// fund-specific conditions applying to every offer
dealManager.setSpvThresholdConditions(conditions);
// conditions evaluated only at finalization
dealManager.setClosingConditions(conditions);
```

These lists accept only contracts implementing the typed `ISecondaryTradingCondition`, and the setters check for it through ERC-165. [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade) shows the lists in use.

## Write a custom condition

`checkCondition` receives the calling contract, the function selector and arbitrary `data`, so a condition can encode any onchain check:

```solidity
contract MinBalanceCondition is ICondition {
    IERC20  immutable token;
    uint256 immutable minimum;
    constructor(IERC20 t, uint256 m) { token = t; minimum = m; }

    function checkCondition(address, bytes4, bytes memory data)
        external view returns (bool)
    {
        address subject = abi.decode(data, (address));
        return token.balanceOf(subject) >= minimum;
    }
}
```

Deploy it and pass its address in any `ICondition` list on this page.


# Run a secondary trade

Post, accept and finalize a secondary offer on a cyberCORP's DealManager under an exemption pathway

A cyberCORP's `DealManager` settles secondary trades of its Ledger Entry Tokens (LETs) through an offer flow: post an offer, accept it, finalize the settlement. Each acceptance creates a settlement agreement in the `CyberAgreementRegistry` (a `bytes32` id), signed by both sides, and the DealManager holds the escrow itself. To settle in scrip instead, see [Scripify and settle a secondary trade](/building-on-the-protocol/how-to/scripify-and-settle).

Offers have two sides (`OfferSide.SELL`, `OfferSide.BUY`) and support partial fills. Every settlement runs under one securities-law exemption pathway (`ExemptionPathway`: `RULE_144`, `SECTION_4A7`, `SECTION_4A1HALF`, `RULE_144A`, `REGULATION_S`), either pinned by the offer or elected by the buyer at acceptance. The structs are in [`ISecondaryTradeStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ISecondaryTradeStorage.sol).

```mermaid
sequenceDiagram
    participant S as Seller
    participant DM as DealManager
    participant B as Buyer
    participant IM as IssuanceManager
    S->>DM: postOffer (SELL)
    Note over DM: units reserved on the seller's LET
    B->>DM: acceptOffer (full or partial)
    Note over DM: buyer elects exemption pathway ·<br/>pathway + SPV conditions checked ·<br/>payment escrowed · settlement agreement fully signed
    B->>DM: finalizeSecondaryTradeAgreement
    Note over DM: SPV + pathway + closing conditions<br/>re-checked at settlement
    DM-->>S: seller proceeds paid · fees distributed
    DM->>IM: secondaryTransfer
    IM-->>B: units onto the buyer's LET
```

## 0. Set the trading policy once

Before any offer can settle, an owner or admin sets the company's trading policy on the DealManager:

```solidity
// enable a pathway and set its exemption-specific conditions
dealManager.setPathwayThresholdConditions(ExemptionPathway.RULE_144, conds, true);
dealManager.setSpvThresholdConditions(fundConds);    // apply to every offer
dealManager.setClosingConditions(closingConds);      // checked at finalize only
dealManager.setMinTradeThreshold(minUnits, minConsideration);
dealManager.setSettlementWindow(window);             // seconds from acceptance
dealManager.setDefaultIntegrator(integrator);        // optional fee-split partner
```

A pathway that is not enabled can be neither pinned nor elected.

The company does not set the platform fee on a settlement. It comes from the DealManagerFactory's **secondary** rate (`getSecondaryFeeRatio()`, which MetaLeX can override per DealManager), set separately from the primary rate used for deals and rounds. A whitelisted integrator's share comes out of that fee. [DealManager](/building-on-the-protocol/reference/contracts/dealmanager) has the fee details.

## 1. Post the offer

```solidity
import {PostOfferParams, OfferSide, ExemptionPathway, HostingMode}
    from "src/interfaces/ISecondaryTradeStorage.sol";

bytes32 offerId = dealManager.postOffer(PostOfferParams({
    side:                     OfferSide.SELL,
    certPrinter:              certPrinter,   // the security being sold
    tokenId:                  tokenId,       // seller's LET (0 for BUY offers)
    units:                    10_000e18,     // 10,000 shares (units are 18-decimal)
    paymentToken:             USDC,
    consideration:            200_000e6,     // total for all offered units
    exemptionPathway:         ExemptionPathway.NONE, // NONE = each buyer elects
    validUntil:               block.timestamp + 30 days,
    counterpartyRestrictions: "",
    additionalTerms:          "",
    integrator:               address(0),    // 0 = default integrator
    templateId:               templateId,    // agreement template
    salt:                     salt,
    globalValues:             globalValues,
    offerorPartyValues:       sellerValues,
    offerorAgreementSig:      sellerAgreementSig, // EIP-712 over the agreement
    openEndorsementSig:       openEndorsementSig, // seller's pre-signed endorsement
    buyerName:                "",            // BUY offers only
    buyerHostingMode:         HostingMode.DIRECT,
    adminMultisig:            address(0)
}));
```

{% hint style="warning" %}
`units` is **18-decimal fixed point** (one share = `1e18`), while `consideration` is denominated in the payment token's own decimals (USDC = 6). Mixing these up misprices the offer by orders of magnitude.
{% endhint %}

* A **SELL** offer requires the caller to be the LET's registered owner and the LET not to be void (`CertificateVoided`). The offered units are reserved on the LET, and reserved units cannot be scripified or reassigned.
* A **BUY** offer pulls the full `consideration` into DealManager custody up front and must pin an exemption pathway.
* `HostingMode.ADMINISTERED` delivers the buyer's lot to `adminMultisig`, so it needs a nonzero address there (`MissingAdminMultisig` otherwise), on a buy offer at posting and on a sell offer at acceptance.
* `offerorAgreementSig` is stored with the offer and attached to each settlement agreement through `signContractWithEscrow`. Neither the DealManager nor the registry verifies it. The offeror's commitment rests on its own `postOffer` transaction (or its relayer authorization), and the stored signature records what it signed.

## 2. Accept all or part of the offer

Any counterparty can accept the whole offer or part of it:

```solidity
import {AcceptOfferParams} from "src/interfaces/ISecondaryTradeStorage.sol";

bytes32 settlementAgreementId = dealManager.acceptOffer(AcceptOfferParams({
    offerId:             offerId,
    units:               4_000e18,                   // 4,000 shares; partial fills allowed
    exemptionPathway:    ExemptionPathway.RULE_144,  // buyer's election (sell offers)
    buyerName:           "Bob Buyer",
    buyerHostingMode:    HostingMode.DIRECT,
    adminMultisig:       address(0),
    sellerTokenId:       0,                          // BUY-offer acceptances only
    acceptorPartyValues: buyerValues,
    acceptorAgreementSig: buyerAgreementSig,         // EIP-712, registry-verified
    openEndorsementSig:  ""                          // BUY-offer acceptances only
}));
```

Acceptance creates the settlement agreement in the registry, signed by both sides, and funds the escrow (on a sell offer the buyer pays here). It also re-runs the SPV and elected-pathway conditions against the actual buyer. A failed condition reverts the whole acceptance, and so does a seller LET voided since the offer was posted.

The registry verifies `acceptorAgreementSig`, so the acceptor signs `SignatureData` (with `signer` = the acceptor) for the settlement agreement's id before the agreement exists. On a registry with the current `SignatureData` type, that id is

```solidity
// A dynamic array, as SecondaryTradeStorage builds it. A literal
// [offeror, acceptor] is address[2], which encodes differently and gives
// another id.
address[] memory parties = new address[](2);
parties[0] = offeror;
parties[1] = acceptor;

keccak256(abi.encode(
    offer.templateId,
    uint256(keccak256(abi.encodePacked(offer.salt, n))), // n = earlier acceptances of this offer
    offer.globalValues,                                  // string[]
    parties,                                             // address[]
    bytes32(0),        // no secret
    dealManager        // the DealManager is the finalizer
))
```

The settlement expiry is not part of the id. `n` counts every earlier acceptance, including voided ones, so a competing acceptance mined first changes the id and invalidates the signature. The id formulas are in [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry).

Each lot is priced from the offer's running total: `offer.consideration × (offer.unitsAccepted + params.units) / offer.units − offer.paymentAccepted`, where `params.units` is this fill's size and `offer.units` the offer's total (integer division rounds down; a negative result counts as zero). The lot that exhausts the offer therefore pays whatever remains, and rounding does not accumulate across fills. A priced lot that rounds to zero reverts `ZeroConsiderationFill`. The minimum-trade threshold applies to a partial lot and to the remainder it leaves. The lot that exhausts the offer is exempt, so raising the threshold never strands an existing tail.

## 3. Finalize the settlement

Anyone can finalize after acceptance, within the settlement window:

```solidity
dealManager.finalizeSecondaryTradeAgreement(settlementAgreementId);
```

Finalization re-checks the pathway, threshold and closing conditions, because eligibility must hold at settlement as well as at acceptance. It then pays the seller net of the secondary fee (any integrator share comes out of that fee), releases the unit reservation, and executes the ownership change through `IssuanceManager.secondaryTransfer`, which reduces the seller's LET and mints the buyer's LET with the seller's endorsement attached.

## Cancel an offer or void a settlement

* `cancelOffer(offerId)` lets the offeror cancel a live offer. It releases only the uncommitted units and consideration, and settlements already in flight resolve on their own.
* `voidSecondaryTradeAgreement(agreementId, signer, signature)` records one party's void request. The settlement voids once **both** parties request it, or once it has expired.
* `voidExpiredSecondaryTradeAgreement(agreementId, signer, signature)` unwinds a settlement past its expiry.
* `syncVoidedSecondaryTradeAgreement(agreementId)` syncs a settlement that was voided directly in the registry.

## Let a relayer submit for the user

`postOffer`, `cancelOffer` and `acceptOffer` each have a relayed overload `(…, address forAddr, uint256 nonce, bytes sig)`, where `sig` is `forAddr`'s EIP-712 authorization over the call parameters and nonce, so end users can trade without paying gas. `voidSecondaryTradeAgreement`'s relayed overload takes `(agreementId, signer, voidSignature, nonce, authSig)`: the registry void signature plus a separate relayer-authorization signature, with `signer` as the authorized party.

## Negotiate a bespoke deal instead

The DealManager's generic deal flow runs alongside the offer flow, for primary issuances and negotiated bilateral deals: `proposeDeal(...)` or `proposeAndSignDeal(...)` (both return `(bytes32 agreementId, uint256[] certIds)`), then `signDealAndPay(signer, agreementId, signature, partyValues, fillUnallocated, name, secret)`, then `finalizeDeal(agreementId)`. `voidExpiredDeal`, `revokeDeal` and `signToVoid` unwind a deal.

The DealManager holds payment and LETs in escrow for deals too, so neither flow has a separate escrow contract to call; [LeXscroWLite](/building-on-the-protocol/reference/contracts/lexscrowlite) documents the escrow logic.

Function-level detail is in [DealManager](/building-on-the-protocol/reference/contracts/dealmanager) and [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry). The conditions in the trading policy are covered in [Gate state transitions with conditions](/building-on-the-protocol/how-to/gate-with-conditions).


# Scripify and settle a secondary trade

Turn part of a LET into scrip, trade the scrip, and convert it back into a LET in the buyer's name

This guide converts part of a Ledger Entry Token (LET) into scrip, the fungible ERC-20 token that tracks the LET's units, transfers the scrip to a buyer, and converts it back into a LET in the buyer's name. To settle on the LET itself through the DealManager's offer flow, see [Run a secondary trade](/building-on-the-protocol/how-to/run-a-secondary-trade).

The example starts from the LET issued in [Incorporate a cyberCORP](/building-on-the-protocol/how-to/incorporate-a-cybercorp): `tokenId = 1` on the Common Stock LET contract at `commonPrinter`, held by `alice`. You also need the company's `issuanceManager` address.

## 1. Deploy the CyberScrip for the LET contract

Each LET contract gets its own `CyberScrip`, deployed with `deployCyberScrip`. The same call sets the scrip ratio, the conditions on conversion in each direction, and which compliance powers exist. The call is `onlyOwner`, so an officer runs it.

```solidity
address cyberScrip = IIssuanceManager(issuanceManager).deployCyberScrip(
    commonPrinter,
    typeRestrictionHooks,   // ITransferRestrictionHook[]
    certToScripConditions,  // ICondition[] gating scripification
    scripToCertConditions,  // ICondition[] gating de-scripification
    1e18,                   // scripToCertMinimum (in scrip, one whole token)
    1,                      // scripRatioNumerator
    1,                      // scripRatioDenominator
    new uint256[](0),       // scripifyWhitelistIds
    false,                  // scripifyWhitelistEnabled
    true,                   // enableForceTransfer
    true,                   // enableForceBurn
    true                    // enableFreeze
);
```

Scrip minted = units × `scripRatioNumerator / scripRatioDenominator`, with no implicit rescaling, so the `1 : 1` ratio makes one share's worth of LET units read as one whole scrip token in wallets.

{% hint style="warning" %}
All LET unit quantities are **18-decimal fixed point** (one share = `1e18` units), and scrip is an 18-decimal ERC-20. Passing raw share counts (`1_000_000` instead of `1_000_000e18`) under-scales by a factor of 10¹⁸.
{% endhint %}

## 2. Scripify part of Alice's LET

Alice, the LET's registered owner, calls `scripifyCert` herself. The IssuanceManager checks `legalOwnerOf` against the caller.

```solidity
IIssuanceManager(issuanceManager).scripifyCert(
    commonPrinter,   // certAddress
    1,               // id (the LET's token id)
    1_000_000e18,    // units to scripify (1,000,000 shares, 18-decimal)
    alice            // recipient of the scrip
);
```

The call reduces the LET's `unitsRepresented`, records the scripified units in the scrip pool, and mints scrip to Alice: `1_000_000e18` base units, or 1,000,000 whole tokens at the `1:1` ratio. Units reserved for pending deals cannot be scripified, and neither can a void LET. The scrip's transfer hooks also apply to this mint, so with a `WhitelistTransferHook` installed, Alice must be whitelisted first. What rights scrip carries is set by the company's governing documents; [LETs and scrip](/how-cybercorps-works/lets-and-scrip) explains how the two forms relate.

## 3. Transfer the scrip to the buyer

```solidity
CyberScrip(cyberScrip).transfer(bob, 1_000_000e18);
```

The transfer must pass every installed transfer hook; see [Restrict scrip and LET transfers](/building-on-the-protocol/how-to/restrict-transfers).

## 4. Pre-approve the buyer's recertification

Bob holds no LET on this contract yet, so an officer sets the details of the LET he will receive when he converts his scrip:

```solidity
IIssuanceManager(issuanceManager).setRecertificationApproval(
    commonPrinter,
    bob,
    "Bob Buyer",
    bobCertDetails,    // CertificateDetails
    officerSignature   // bytes
);
```

## 5. Convert the scrip back into a LET

Bob presents his scrip:

```solidity
IIssuanceManager(issuanceManager).convertScripToCert(
    commonPrinter,   // certAddress
    1_000_000e18     // amount of scrip to present
);
```

The call burns Bob's scrip, withdraws the units from the scrip pool, and registers Bob as holder of record on a LET with the approved details. The conversion enforces the approval itself: if the caller holds no active LET on that contract, the call reverts `RecertificationApprovalRequired` unless an officer's `setRecertificationApproval` for the caller is on file. If the caller already holds an active LET, the units are added to it instead. An issuer that wants an admin-managed approval list as well can add an `IssuerApprovalRecertificationCondition` to the `scripToCertConditions`.

Function-level detail is in [CyberScrip](/building-on-the-protocol/reference/contracts/cyberscrip) and [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager).


# Convert SAFEs to equity

The intended SafeCertificateConverter flow for converting SAFE LETs into equity in a priced round; the converter is a stub

When a cyberCORP runs a priced round, its outstanding SAFEs convert to equity. Each SAFE is recorded as a Ledger Entry Token (LET) on the company's SAFE LET contract, and `SafeCertificateConverter` is meant to compute each one's conversion plan from the round's data.

{% hint style="warning" %}
`SafeCertificateConverter` is a stub. The body of `computeConversion` is commented out in the source, and the function returns an empty plan. Do not rely on it for live conversions. This guide describes the intended use; see [SafeCertificateConverter](/building-on-the-protocol/reference/contracts/safecertificateconverter).
{% endhint %}

## Compute the conversion plan

```solidity
import {ICertificateConverter} from "src/interfaces/ICertificateConverter.sol";

ConversionPlan memory plan = ICertificateConverter(CONVERTER).computeConversion(
    roundManager,   // the RoundManager running the priced round
    roundId,        // bytes32, the priced round
    certPrinter,    // the SAFE LET contract
    tokenId         // the SAFE LET to convert
);
```

The converter is designed to read the SAFE LET's `investmentAmountUSD` and `issuerUSDValuationAtTimeOfInvestment`, the round's price and cap table snapshot, and apply the round's rounding policy to produce a share count and a target class and series.

## Execute the plan

Issue the new equity LETs through the IssuanceManager (see [Issue a LET](/building-on-the-protocol/how-to/issue-a-let)) and void the SAFE LETs with `voidCert` on the SAFE LET contract.

Function-level detail is in [SafeCertificateConverter](/building-on-the-protocol/reference/contracts/safecertificateconverter) and [RoundManager](/building-on-the-protocol/reference/contracts/roundmanager).


# Sign a cyberAgreement

Register a template, create a multi-party agreement, collect EIP-712 signatures and finalize or void it in the CyberAgreementRegistry

cyberSign runs on the `CyberAgreementRegistry`, which holds agreement templates and the executed contracts made from them. Deals and rounds create their agreements in the same registry.

## 1. Register a template

A template is a reusable legal document with a field schema. Anyone can register one. Ids are first-come, and a collision reverts `TemplateAlreadyExists`.

```solidity
ICyberAgreementRegistry(registry).createTemplate(
    templateId,        // bytes32, the id you choose
    "Series A Stockholder Consent v1",  // title
    "ipfs://...",      // legalContractUri (canonical text)
    globalFields,      // string[], fields common to the contract
    partyFields        // string[], fields filled per signing party
);
```

A one-off agreement can skip this step. `createStandaloneContractAndSign(title, legalContractUri, globalFields, partyFields, salt, globalValues, parties, partyValues, expiry, signature)` derives a template id from the content, creates the template if it does not exist, creates the contract and records the proposer's signature, all in one transaction.

## 2. Create a contract from the template

```solidity
bytes32 contractId = ICyberAgreementRegistry(registry).createContract(
    templateId,
    salt,           // uint256
    globalValues,   // string[]
    parties,        // address[]
    partyValues,    // string[][], per-party values
    secretHash,     // bytes32
    finalizer,      // address allowed to finalize
    expiry          // uint256
);
```

`createContract` returns the id, and the registry emits it in `ContractCreated`. An `eth_call` of the same `createContract` predicts it before you send. When a party must sign before the agreement exists (the proposer of a standalone agreement, for example), compute the id as `keccak256(abi.encode(templateId, salt, globalValues, parties, secretHash, finalizer))`. For a standalone agreement, use `secretHash = 0`, `finalizer = address(0)` and the derived template id.

A registry with the older signature type, whose `SIGNATUREDATA_TYPEHASH()` returns `0x49ba7af1…0da4`, derives the id from four fields only: `keccak256(abi.encode(templateId, salt, globalValues, parties))`. The separate zkSync Era deployment is one. Both registry types report `version()` `"1"`, so only the typehash tells them apart.

## 3. Collect each party's signature

Every signing entry point carries the signer's EIP-712 signature over the agreement content. On a registry with the current type, the typed data is:

```ts
// viem signTypedData
const domain = {
  name: "CyberAgreementRegistry",
  version: "1",
  chainId,
  verifyingContract: registry,
};
const types = {
  SignatureData: [
    { name: "contractId", type: "bytes32" },
    { name: "signer", type: "address" },      // the party this signature counts for
    { name: "legalContractUri", type: "string" },
    { name: "globalFields", type: "string[]" },
    { name: "partyFields", type: "string[]" },
    { name: "globalValues", type: "string[]" },
    { name: "partyValues", type: "string[]" },
  ],
};
// message: the template's URI and fields, the agreement's global values,
// and this party's own partyValues, with signer = the party's address
```

Set `signer` to the party's address even when a delegate produces the signature; the registry accepts the signature for that party only. Read `SIGNATUREDATA_TYPEHASH()` on the registry before signing. `0xe37d17c3…6f05` means the type above. `0x49ba7af1…0da4`, the older type used by the zkSync Era deployment, expects the same struct without `signer`.

The registry has three signing entry points:

* `signContract(contractId, partyValues, signature, fillUnallocated, secret)` records the caller's own signature.
* `signContractFor(signer, contractId, partyValues, signature, fillUnallocated, secret)` relays another party's EIP-712 signature.
* `signContractWithEscrow(escrowSigner, contractId, partyValues, signature, fillUnallocated, secret)` applies a pre-escrowed signature. Only the contract's finalizer can call it, so the agreement must name a nonzero finalizer, such as a DealManager or RoundManager (`FinalizerNotDefined` otherwise).

When every party has signed, the registry emits `ContractFullySigned`.

A party can delegate signing authority with `setDelegation(delegate, expiry)` (`expiry == 0` means no expiry) and withdraw it with `revokeDelegation()`. The delegate signs `SignatureData` with `signer` set to the delegating party.

## 4. Finalize the contract

A contract created with `finalizer == address(0)` finalizes automatically when the last party signs. Otherwise the finalizer calls:

```solidity
ICyberAgreementRegistry(registry).finalizeContract(contractId);
```

## Void an agreement

`voidContractFor(contractId, party, signature)` records a party's void request. The signature is an EIP-712 `VoidSignatureData(bytes32 contractId,address party)` signature by the party or its delegate, and the finalizer can submit a request with no signature. The contract voids when every allocated party requests it (unfilled open slots don't count toward unanimity), when its nonzero expiry has passed, or when the first party requests it while only one signature has been collected.

{% hint style="info" %}
An agreement created with `expiry == 0` has **no deadline**. It never counts as expired, so it voids only by unanimous request, or by the proposer while it is the only signer. With a nonzero expiry, the unanimity requirement holds only **until that expiry passes**; after that, a single party's request voids an unfinalized agreement.
{% endhint %}

## Check an agreement's status

Read status with `hasSigned`, `allPartiesSigned`, `isFinalized`, `isVoided`, `getContractDetails` and `getAgreementsForParty`.

Function-level detail is in [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry), and MetaLeX's template library is in [Agreement templates](/building-on-the-protocol/reference/templates).


# Integrate from a frontend

Read and write the cyberCORPs contracts from a TypeScript or React app, build EIP-712 signatures, and pick the right ABI for v4 and v5 companies

This guide covers calling the protocol from a TypeScript or React app. MetaLeX's reference front ends are in [`metalex-webapp`](https://github.com/MetaLex-Tech/metalex-webapp).

## Use wagmi, viem and an indexer

* **wagmi and viem** for contract calls and typed ABIs.
* A wallet connector (injected wallets, WalletConnect).
* An **indexer** for list and aggregate reads such as cap tables and rounds, instead of many direct contract reads.

## Read contract state

```ts
import { useReadContract } from "wagmi";
import { cyberCorpAbi } from "@/abis";

const { data: name } = useReadContract({
  abi: cyberCorpAbi,
  address: cyberCorpAddress,
  functionName: "cyberCORPName",
});
```

`CyberCorp`'s getters are `cyberCORPName`, `cyberCORPType` and `cyberCORPJurisdiction`. On a Ledger Entry Token (LET) contract (`LedgerEntryToken`), `legalOwnerOf` returns a LET's registered owner and `ownerOf` the wallet holding it. v4 and v5 LET contracts have different ABIs; see [Pick the ABI for each company and component](#pick-the-abi-for-each-company-and-component).

## Send transactions through the manager contracts

Use `useWriteContract`. State-changing calls go to the company's manager contracts: `IssuanceManager`, `DealManager` and `RoundManager`. Most `LedgerEntryToken` and `CyberScrip` functions are `onlyIssuanceManager`, with some admin-gated exceptions on the LET contract. The DealManager's secondary-trade entry points (`postOffer`, `acceptOffer`, `cancelOffer`) each have a relayed overload `(…, forAddr, nonce, sig)` for gasless trading. `voidSecondaryTradeAgreement`'s relayed form takes `(agreementId, signer, voidSignature, nonce, authSig)` instead.

## Batch the formation fee and deployment with Multicall3

A USDC platform fee and a `CyberCorpFactory` deployment can go in one transaction through the canonical Multicall3 (`0xcA11bde05977b3631167028862bE2a173976CA11`). The fee travels as an EIP-3009 `transferWithAuthorization` (the user signs typed data naming the recipient, amount, validity window and a random nonce), and the formation call takes the owner as a parameter. Neither inner call depends on `msg.sender`, so Multicall3 being the sender of both is harmless.

The batch is all-or-nothing **only if you set `allowFailure: false` on both `Call3` entries**, which makes `aggregate3` revert the whole transaction when either call fails. With `allowFailure: true`, Multicall3 continues past a failed call. The suite's `test_frontRunAuthorizationToleratedWithAllowFailure` shows formation succeeding while the fee call fails, which as a frontend default would deploy the company without collecting the fee.

Anyone can submit an EIP-3009 authorization, including a front-runner who mines it directly before your batch. The fee then reaches its named recipient, and the strict batch reverts on the consumed nonce with nothing deployed (`test_frontRunAuthorizationRevertsTheStrictBatch`). A reverted batch can therefore still have moved the fee, so detect the used nonce, verify where the fee went, and reconcile before asking the user to sign a fresh authorization. [`test/MulticallFormationFeeForkTest.t.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/test/MulticallFormationFeeForkTest.t.sol) is a worked Base-mainnet example, including the `TransferWithAuthorization` typed-data struct.

## Build EIP-712 signatures

cyberRAISE EOIs, deal countersignatures and cyberSign agreements are EIP-712 typed-data signatures, produced with viem's `signTypedData`. The `CyberAgreementRegistry` underlies all of them. A round EOI and a deal both resolve to a registry contract identified by a `bytes32` id, and the party signs the registry's `SignatureData` under the domain `{ name: "CyberAgreementRegistry", version: "1", chainId, verifyingContract: registry }`.

The `SignatureData` type comes from the registry implementation and is independent of the company's version. Read it before building the typed data:

```ts
const typeHash = await publicClient.readContract({
  address: registry,
  abi: cyberAgreementRegistryAbi,
  functionName: "SIGNATUREDATA_TYPEHASH",
});
// 0xe37d17c3ab7740aee31093101f9d27d139a5c3b35324b266efe5d085d6486f05
//   current type: SignatureData(bytes32 contractId,address signer,string legalContractUri,
//   string[] globalFields,string[] partyFields,string[] globalValues,string[] partyValues)
// 0x49ba7af1fd9b42077b5e2bf090b7deb0f443e9ae99b46ce66b4859e28d670da4
//   older type: the same struct without `signer`
// anything else: refuse rather than guess
```

The registry at `0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134` on Ethereum, Base and Arbitrum uses the current type, and the separate zkSync Era registry uses the older one. With the current type, set `signer` to the party the signature counts for (the delegator when a delegate signs). Where the flow allows, take agreement ids from the `ContractCreated` event or a simulated `createContract`. When a party must sign first, hash the id with the formula for that registry's type, given in [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement) and [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry).

Other signatures use their own domains, each with version `"1"`, the chain id and the verifying contract:

| Signature                                                                                             | Domain name                                   | Verifying contract |
| ----------------------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------ |
| Officer's escrowed round signature (`EscrowedSignatureData`)                                          | `"RoundManager"`                              | the RoundManager   |
| Relayed `postOffer` / `acceptOffer` / `cancelOffer` / void authorizations                             | `"DealManager"`                               | the DealManager    |
| Officer's deployment-metadata signature (`RoundSupplementalData`) for `deployCyberCorpAndCreateRound` | `"CyberCorpFactory"` (or `"PumpCorpFactory"`) | the factory        |

## Render a LET

`tokenURI(tokenId)` on the LET contract returns a base64 `data:` JSON whose `image` is an onchain-rendered SVG. Decode the JSON, then render the SVG. The LET contract has no getter for the raw JSON, so base64-decode the `data:` payload to get it. Amounts in the metadata are formatted for display, so read exact amounts with `getCertificateDetails(tokenId)` on the LET contract. [CertificateUriBuilder](/building-on-the-protocol/reference/contracts/certificateuribuilder) documents the JSON fields, the certificate image and how amounts are formatted.

## Pick the ABI for each company and component

v4 and v5 companies run side by side on the same chains. The factories on Ethereum, Base and Arbitrum create v5 companies, and an existing company keeps its deployed version until its owners upgrade it. Pick the ABI per company and per component, never per chain:

* Read `DEPLOY_VERSION()` on the contract you are about to call: `CyberCorp`, `IssuanceManager`, `DealManager`, `RoundManager`, `LedgerEntryToken` or `CyberScrip`. v5 components report `"5"`. v4 components report `"4"`, `"4.1"` (`IssuanceManager`) or `"4.0.1"` (`DealManager`). Compare the major version, and refuse a version your ABIs do not cover instead of falling back to another.
* Each component upgrades on its own, and nothing onchain forces a company to upgrade them together, so do not infer one component's version from another's.
* Selectors differ between versions wherever a struct differs. In v5, `CyberCertData` and `IssuanceManager.createCertPrinter` carry the extension's `seriesData`: `createCertPrinter` is `0x6cf6f4b0` on a v4 IssuanceManager and `0xfe197ea2` on a v5 one. A call with the other version's selector reverts.
* The agreement registry is a single proxy per chain, shared by v4 and v5 companies. Select its signature type as in [Build EIP-712 signatures](#build-eip-712-signatures).
* On a v5 factory, `CyberCorpFactory.deployCyberCorpAndCreateRound` takes a second officer signature (`metadataSignature`, signed under the `"CyberCorpFactory"` domain) and its selector is `0x80d78d40`.

Regenerate ABIs from the source when implementations change.

[Building on the protocol](/building-on-the-protocol/how-to) maps each MetaLeX product to the contracts it calls, and [Using the apps](/using-the-apps/webapp) covers the live apps from a user's side.


# Upgrade a cyberCORP

Move an existing cyberCORP to v5 by upgrading its four proxies and two beacons together, then backfill and open its LET contracts

A company deployed through the `CyberCorpFactory` stack puts its CyberCorp, IssuanceManager, DealManager and RoundManager behind UUPS proxies. Its Ledger Entry Token (LET) contracts and `CyberScrip` instances use beacon proxies whose beacons the IssuanceManager owns. An upgrade needs both sides: MetaLeX publishes a reference implementation on the factory, and the company's owner opts in. See [Upgrade model](/building-on-the-protocol/reference/upgrade-model).

New companies are created on v5. An existing company stays on the version it was deployed with until its owner runs this upgrade. In the cyberCORPs app, an available upgrade appears as the first task in mission control and links to the app's **Upgrade** page; [Run your company](/using-the-apps/company) covers the app side.

## 1. Check the company's versions and targets

Read `DEPLOY_VERSION()` on the CyberCorp, IssuanceManager, DealManager and RoundManager, and on the implementations behind the IssuanceManager's two beacons (`getCertPrinterBeaconImplementation()`, `getScripBeaconImplementation()`). Then read the targets from the factory each contract records:

```solidity
address corpTarget = ICyberCorpSingleFactory(CyberCorp(cyberCorp).upgradeFactory())
    .getRefImplementation();
IIssuanceManagerFactory imf =
    IIssuanceManagerFactory(IIssuanceManager(issuanceManager).getUpgradeFactory());
address imTarget      = imf.getRefImplementation();
address printerTarget = imf.getCyberCertPrinterRefImplementation();
address scripTarget   = imf.getCyberScripRefImplementation();
// DealManager and RoundManager: getRefImplementation() on the
// DealManagerFactory and RoundManagerFactory they were deployed from.
```

If a target is not what you expected, do not upgrade. The reference-implementation check exists so that neither side can move you to an arbitrary implementation.

{% hint style="warning" %}
This path needs the CyberCorp, IssuanceManager and DealManager to be UUPS proxies. The oldest cyberCORPs run those three behind MetaLeX-owned legacy beacons (`DEPLOY_VERSION` `"3"`). `upgradeToAndCall` on them reverts `UUPSUnauthorizedCallContext`, and the company cannot upgrade them itself. See [Upgrade model](/building-on-the-protocol/reference/upgrade-model).
{% endhint %}

## 2. Upgrade all six contracts in one batch

An existing UUPS company moves to v5 with six calls, made by an owner of the company's BorgAuth (level 99 or above; officers hold 200):

```solidity
UUPSUpgradeable(cyberCorp).upgradeToAndCall(corpTarget, "");
UUPSUpgradeable(issuanceManager).upgradeToAndCall(imTarget, "");
UUPSUpgradeable(dealManager).upgradeToAndCall(dealManagerTarget, "");
UUPSUpgradeable(roundManager).upgradeToAndCall(roundManagerTarget, "");
IIssuanceManager(issuanceManager).upgradeCertPrinterBeaconImplementation(printerTarget);
IIssuanceManager(issuanceManager).upgradeScripBeaconImplementation(scripTarget);
```

Each call is `onlyOwner` and reverts `NotRefImplementation` unless its target equals the published reference. Upgrading a beacon moves every instance of that type under the IssuanceManager at once.

Run the six as one transaction where you can; for a Safe-owned company, that is one Safe batch. Each version calls functions the other version lacks, so a company left part-way (a v5 IssuanceManager next to a v4 DealManager, for example) can see deal, round and issuance calls revert until the rest land. The contracts repository's `script/upgrade-v5.s.sol` builds this batch for one company (`corpUpgradeCalls`, and `printCorpUpgradeSafeBatch`, which prints Safe Transaction Builder JSON) and refuses a company that has no RoundManager.

The app's **Upgrade** page runs the same calls in the same order. If a Safe that the connected wallet owns holds the owner role, it proposes every call that is not done as one Safe transaction, followed by the LET migrations of step 3 that need no dates. Its gas grows with the holders (one `initializeHolderCount` each) and the lots (each backfill touches each lot). The page estimates it from bounds measured on a Base fork and does not offer a batch above half the block gas limit; such a company needs a plan with MetaLeX, because running the migrations after the upgrade in a separate transaction reopens the period described in step 3. Otherwise it sends one call at a time from a wallet with the owner role, and only the next call is available. From a wallet, the LET migrations follow in later transactions, and in between the v5-only paths (for example a secondary offer that a buyer accepts and settles with `finalizeSecondaryTradeAgreement`) can mint a LET to a holder that is not seeded yet. Nothing outside the contracts can stop those calls, so use a Safe batch for a company whose LET contracts have holders from before the upgrade, and read step 3 first. It starts the sequence only for a company with all contracts on a v4 release (any 4.x `DEPLOY_VERSION`, such as `"4"`, `"4.0.1"` or `"4.1"`), UUPS proxies, an IssuanceManager and a DealManager, and v5 references for every call. A v4 company without a RoundManager takes the five other calls; a fork test of a deal and an issuance after those five calls passed before the app allowed it. While a company runs v5 and older contracts side by side, the app refuses new deals, rounds and issuance. See [Run your company](/using-the-apps/company#upgrade-an-existing-company-to-v5).

## 3. Prepare the upgraded LET contracts

The upgraded LET contracts keep their data, but v5 adds storage that starts empty, zero or closed. Work through the steps below on each LET contract as soon as the upgrade lands, because holders can call `convertScripToCert` at any time. [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) documents each function and the holder-counting rules.

The app's Upgrade page runs these steps from a checklist for each LET contract once the company runs v5. It reads which lots and holders still need each step from the contract's storage. It sends the required calls in this order: holder counters, legal-owner backfill, the badge when the company has a `HolderCapCondition` configuration, tally backfill. It refuses issuance, deal settlement and scrip conversion on a LET contract until its holder counters, legal-owner index and holder tally are complete. It proposes dates from onchain and company records, and sends them only after the owner confirms them.

Existing LET contracts keep the certificate extension they were created with. New LET contracts can bind the [V3 extensions](/building-on-the-protocol/reference/extensions), which need a v5 LET contract and IssuanceManager.

### Seed each holder's possession counter

On a LET contract whose lots were minted before it kept a per-wallet possession counter, a holder's counter reads zero and a transfer out of that wallet reverts. An admin seeds each such holder with `initializeHolderCount(holder)` before anything is minted or transferred to or from it. Seed the DealManager and RoundManager last when they hold lots in escrow: a pending deal or allocation settles by a transfer out of them, which reverts while their counter is zero, so no settlement can reach a holder that is not seeded yet. The app's checklist uses this order.

A mint or transfer that reaches a holder first sets the counter to 1, below the holder's balance. `initializeHolderCount` then reverts `HolderCountAlreadyInitialized`, and no deployed function corrects the counter. A holder can cause this without the company: `convertScripToCert` looks up the holder's lot through the legal-owner enumeration, which is empty until the backfill below. With a recertification approval on file, it then mints a new LET to the holder. An FCFS round that has not ended does the same without the company once it opens: `RoundManager.submitEOI` is public, and for an FCFS round it calls `allocate` in the same transaction, which mints a LET to the investor. A new LET also reaches a legacy holder indirectly: if the LET contract is `transferable` (or a lot has `setTokenTransferable` on), any holder whose counter is already seeded can transfer possession to a legacy holder without an endorsement. No seeding order prevents this, so turn transfers off (`setGlobalTransferable(false)` and each lot's override) until every counter is seeded. Seed the counters in the same transaction as the beacon upgrade where you can, as the app's Safe batch does. A batch is built when it is proposed, so a lot minted while it waits for signatures is not in it. So for a wallet upgrade and for a Safe batch, close every FCFS round that has not ended, on every Round Manager the company has used, including one that has not started, with `closeRoundNow(roundId)`, and clear every outstanding approval with `clearRecertificationApproval(certAddress, investor)`, before the upgrade. Also settle or void every pending deal and secondary offer whose lots the DealManager holds: `finalizeDeal` is public, and `processTransfer` lets the current DealManager deliver even with transfers off, so a delivery after the batch was encoded reaches a holder that the batch does not seed. Check all of these again right before the Safe executes the batch, and do not issue or propose deals while it waits for signatures; the app's Upgrade page refuses to build or show the batch until they hold. `closeRoundNow` sets `endTime` to the current block's timestamp, and `submitEOI` reverts `RoundNotOpen` only when `block.timestamp > endTime`. So wait for a block with a later timestamp before the beacon upgrade. `closeRoundNow` reverts `EndTimeReductionRestricted` for a round that restricts it; for such a round, wait until the round ends. Set the approvals again only after the counters are seeded and the legal-owner enumeration is backfilled (see below), because until then a conversion with an approval also draws on the shared vault.

### Backfill legal owners and the look-through tally

The legal-owner enumeration and the look-through holder tally start empty. Until they are backfilled, existing holders read zero through `balanceOfLegalOwner` and `isLegalHolder`, and `lookThroughHolderCount()` starts at zero, so a configured `HolderCapCondition` can admit a buyer although the real cap is already full.

Scrip redemption depends on the enumeration too: `convertScripToCert` finds the holder's lot and draws down the holder's own vault positions through it. Before the backfill it cannot find the lot, and reverts for a missing recertification approval. With an approval on file, it takes the withdrawal from the shared vault instead of the holder's positions. Run these steps before enabling registration or secondary settlement on the LET contract:

1. If the SPV uses look-through counting, wire the badge first with `setLookThroughBadge(badge)`.
2. Run `backfillLegalOwners(start, count)` and `backfillLookThroughTally(start, count)` in batches over `[0, totalSupply())`. Both are permissionless and idempotent, so a batch can be re-run safely.

### Set acquisition timestamps for Rule 144 and Reg S trades

Each lot's `acquisitionTimestamp` reads zero for lots minted before the upgrade. `HoldingPeriodCondition` (Rule 144) and `RegSDistributionComplianceCondition` fail closed on a zero timestamp, so secondary trades on those pathways stay refused until it is set. Before enabling them:

1. On a LET contract whose extension supports the fund-interest type, run `backfillAcquisitionTimestamps(start, count)` in batches. It copies each lot's recorded acquisition date, and does nothing on other LET contracts.
2. For the remaining lots, an admin sets each known date with `setAcquisitionTimestamp(tokenId, ts)`. The date anchors the holding period, so take it from the company's records.

### Record issue dates for the certificate image

v4 LET contracts never stored `issueTimestamp`, so it reads zero for every lot minted before the upgrade, and the certificate image built by `CertificateUriBuilder` shows a blank **Issue Date** for each one. Nothing backfills it automatically. An admin sets each lot's issuance date with `setIssueTimestamp(tokenId, ts)`, taking the date from the company's records (for example the lot's original issuance endorsement). The date fixes the certificate's displayed record and blocks no transfer or trade.

### Open the registration gate

The v5 registration gate is new storage and starts closed. Until an admin calls `setGlobalLegalTransferable(true)` on a LET contract (or `setTokenLegalTransferable` per lot), `assignCert`, endorsed transfers and secondary-trade settlement on it revert `LegalOwnerNotTransferable`. Issuance and the managers' deliveries out of escrow are not affected. Open the gate after the legal-owner backfill.

## Who controls the upgrade

* The company may stay on its current implementation indefinitely.
* An upgrade to anything other than the factory's published reference reverts.
* The company's owner makes the upgrade calls. The deploying factory may still hold owner level on your BorgAuth; [Access control](/building-on-the-protocol/reference/access-control) shows how to check.

[Upgrade model](/building-on-the-protocol/reference/upgrade-model) has the version and proxy details, and [Upgrades and control](/how-cybercorps-works/upgrades-and-control) explains why an upgrade needs both MetaLeX and the company.


# Deploy a PumpCorp for ACE

Deploy a cyberCORP and its ACE round in one transaction through PumpCorpFactory

**ACE** (Asset Conversion to Equity) is a structured offering, typically under Regulation S and open to non-US persons, in which investors arrive holding a community's tokens and leave holding ACE SAFEs in the issuing corporation. The **`PumpCorpFactory`** ([`src/PumpCorpFactory.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/PumpCorpFactory.sol)) and an ACE-configured SAFE extension run it. The live product is [ace.metalex.tech](https://ace.metalex.tech).

## How PumpCorpFactory deploys an offering

`PumpCorpFactory` uses the same primitives as `CyberCorpFactory`. It deploys a cyberCORP suite configured for an ACE offering and creates the round. `deployCyberCorp` deploys the suite only, and `deployCyberCorpAndCreateRoundFor` deploys the suite and creates the round in one transaction. The source also contains `deployCyberCorpAndCreateOffer`, which is marked work in progress and is not in use.

`deployCyberCorpAndCreateRoundFor` checks that the round's first two party values are the officer's name and address, and verifies two officer signatures:

* the escrowed EIP-712 signature over the round economics (domain `"RoundManager"`), which the new RoundManager checks, and
* `metadataSignature`, an EIP-712 signature under the domain `"PumpCorpFactory"` over the deployment details the escrowed signature does not cover: the corp salt, payout address, round flags, officer, company details, extension data, round party values, legal details, the `CyberCertData` entries and condition addresses (`InvalidMetadataSignature` otherwise).

Deployment addresses are bound to the configuration. The factory hashes the caller's salt with the company details, payout address and officer (`computeDeploymentSalt`) before deploying, and each component factory namespaces the salt by its caller, so another deployer cannot take the predicted addresses with different terms.

## Run the offering

The flow follows a standard cyberRAISE round:

1. Deploy the PumpCorp through `PumpCorpFactory` with the offering parameters: pricing, cap, the agreement template, and a zkPassport-based non-US `ICondition` for Regulation S gating.
2. Investors submit EOIs and are allocated as in [Run a cyberRAISE round](/building-on-the-protocol/how-to/run-a-cyberraise-round).
3. On allocation, each investor receives an ACE SAFE as a Ledger Entry Token (LET) whose security series is `SecuritySeries.ACE`.

The `deploy*` parameter lists are long and are not reproduced here. The contract source and the deploy scripts ([`script/deploy-pump-factory.s.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/script/deploy-pump-factory.s.sol), [`script/deploy-pump-factory-full-lifecycle.s.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/script/deploy-pump-factory-full-lifecycle.s.sol)) are the authoritative reference for the ACE deployment shape.

See also [Factories](/building-on-the-protocol/reference/factories), [Security types](/building-on-the-protocol/reference/security-types), and [ACE](/using-the-apps/ace) for the investor app.


# Deploy a MetaDAO SPC

Deploy a futarchy-governed Cayman segregated portfolio company and its SegCos through MetaDAOFactory

A **MetaDAO** is a futarchy-governed entity structured as a Cayman Segregated Portfolio Company (SPC), and each of its portfolios (SegCos) has its own futarchy oracle. The **`MetaDAOFactory`** ([`src/MetaDAOFactory.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/MetaDAOFactory.sol)) deploys it.

You need legal counsel familiar with Cayman SPCs and futarchy governance, and the MetaDAO Futarchy Governance templates from [`/templates`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/templates/README.md) (search for `MetaDAO Futarchy Governance SPC`).

## How MetaDAOFactory deploys the SPC

`MetaDAOFactory` deploys cyberCORP suites configured as an SPC-style entity through three entry points:

* `createParentCorp(...)` creates the MetaDAO parent corp. Only the factory's owner can call it, and a second call reverts `ParentCorpAlreadyCreated`.
* `deployMetaCorp(...)` deploys a suite (BorgAuth, CyberCorp, IssuanceManager, DealManager) with the same parameter shape as `CyberCorpFactory.deployCyberCorp`.
* `deployMetaDAOContractFor(...)` deploys a SegCo suite and executes the SegCo and board-consent agreements in one transaction. It takes a `_segCoTemplateId` and `_boardConsentTempateId` (spelled that way in the source) plus the deployer officer's values and EIP-712 signature.

Board-consent agreements recorded through the [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry) approve each new SegCo. The deploy script ([`script/deploy-metadao-factory.s.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/script/deploy-metadao-factory.s.sol)) seeds the registry with the SegCo and Board Consent templates.

The factory's full parameter lists are not reproduced here. The contract source is the authoritative reference for the MetaDAO deployment shape.

See also [Factories](/building-on-the-protocol/reference/factories), [Agreement templates](/building-on-the-protocol/reference/templates), [Legal mappings](/how-cybercorps-works/legal-mappings), and [Launchpads](/using-the-apps/launchpads) for the MetaDAO formation pages in the app.


# Deploy a LiquiLeX pool

Pair a company's scrip against a stablecoin in a Uniswap v4 pool with MetalexIssuerFeeHook, under a whitelisted or open compliance model

**LiquiLeX** provides AMM liquidity for a company's scrip in Uniswap v4 pools, with the **`MetalexIssuerFeeHook`** routing swap fees to MetaLeX and the issuer.

## Choose a compliance model

You need a cyberCORP with a deployed `CyberScrip` for the class you want to make liquid (see [Scripify and settle a secondary trade](/building-on-the-protocol/how-to/scripify-and-settle)), and one of two compliance models:

* **Whitelisted pool.** A `WhitelistTransferHook` on the scrip, with the pool's custody addresses whitelisted. Only whitelisted addresses can send or receive the scrip, so credential checks happen when an address is added to the whitelist.
* **Open pool.** No transfer hook. Compliance is enforced when scrip converts back into a Ledger Entry Token (LET), optionally with a zkPassport check at the swap.

## Deploy the hook and the pool

1. Deploy the `MetalexIssuerFeeHook` ([`src/hooks/uniswap/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/hooks/uniswap/README.md)) and call `initialize(auth, poolManager)`. The hook declares `beforeSwap`/`afterSwap` permissions (with return deltas), and Uniswap v4 requires the hook address to encode those flags, so deploy it at a mined address with the standard `HookMiner` salt-mining flow.
2. Initialize a Uniswap v4 pool pairing the `CyberScrip` against a stablecoin, with `hooks` set to the deployed fee hook.
3. As a BorgAuth admin, configure the pool's fees with `setPoolConfig(key, metalexRecipient, issuerRecipient, metalexFeeBps, issuerFeeBps, enabled)`. Both fees are in basis points, and together they can be at most `10_000`.
4. Seed liquidity. If the scrip has a `WhitelistTransferHook`, whitelist every address the scrip passes through: in Uniswap v4 that is the singleton `PoolManager`, which holds every v4 pool's balances, plus any router or position manager that takes custody of the scrip in your flow. Scrip hooks also run when scrip is minted, so every address that receives scrip from `scripifyCert` must be whitelisted as well.

See also [Hooks](/building-on-the-protocol/reference/hooks), [CyberScrip](/building-on-the-protocol/reference/contracts/cyberscrip) and [Composability](/how-cybercorps-works/composability).


# Reference

Contract APIs, roles, events and deployed addresses

Look up contract functions, access rules, events, errors and addresses here. The [guides](/building-on-the-protocol/how-to) show how to put them together.

> **The contract source is authoritative.** These pages follow the `develop` branch of [`cybercorps-contracts`](https://github.com/MetaLex-Tech/cybercorps-contracts), where `CyberCorp`, `IssuanceManager`, `DealManager`, `RoundManager`, `LedgerEntryToken` and `CyberScrip` all report `DEPLOY_VERSION` `"5"`. Companies that have not upgraded run v4 components (`"4"`, with `IssuanceManager` at `"4.1"` and `DealManager` at `"4.0.1"`), and the pages mark v4 differences wherever an integrator can meet both. `CyberShares` and `SafeCertificateConverter` are partly stubbed, as their pages say. Check the `.sol` source before relying on an exact signature, and treat the code in the guides as an outline of each flow that you verify against the source before use.

## Contents

| Page                                                                            | What it covers                                                                            |
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [Core contracts](/building-on-the-protocol/reference/contracts)                 | Every contract and its role, with a page per contract.                                    |
| [Factories](/building-on-the-protocol/reference/factories)                      | `CyberCorpFactory`, its sub-factories and the specialised factories.                      |
| [Certificate extensions](/building-on-the-protocol/reference/extensions)        | Per-security-type metadata contracts and how their scopes merge.                          |
| [Hooks](/building-on-the-protocol/reference/hooks)                              | Transfer-restriction hooks and the LiquiLeX fee hook.                                     |
| [Conditions](/building-on-the-protocol/reference/conditions)                    | The `ICondition` and `ISecondaryTradingCondition` interfaces and the built-in conditions. |
| [Access control (BorgAuth)](/building-on-the-protocol/reference/access-control) | The numeric role model and the levels the protocol assigns.                               |
| [Upgrade model](/building-on-the-protocol/reference/upgrade-model)              | UUPS and beacon proxies, co-approval, versions in production.                             |
| [Agreement templates](/building-on-the-protocol/reference/templates)            | The `/templates` library and custom templates.                                            |
| [Security types](/building-on-the-protocol/reference/security-types)            | The `SecurityClass`, `SecuritySeries` and `SecurityStatus` enums.                         |
| [Deployments](/building-on-the-protocol/reference/deployments)                  | Contract addresses by chain.                                                              |
| [Glossary](/building-on-the-protocol/reference/glossary)                        | Protocol and app terms.                                                                   |


# Core contracts

What each core contract does and how the suite fits together

Each company-owned contract carries a `DEPLOY_VERSION` constant. In the `develop` source of [`cybercorps-contracts`](https://github.com/MetaLex-Tech/cybercorps-contracts) all six (CyberCorp, IssuanceManager, DealManager, RoundManager, LedgerEntryToken, CyberScrip) report `"5"`, the version deployed on Ethereum, Base and Arbitrum. New companies are created on v5. An existing company runs the version it was created with (`"3"`, `"4"` or `"4.1"`) until its owner upgrades it, so read `DEPLOY_VERSION()` on the instance before choosing an ABI. See [Upgrade model](/building-on-the-protocol/reference/upgrade-model).

```mermaid
flowchart TD
    F["CyberCorpFactory"] --> AUTH["BorgAuth<br/>(numeric-level authority)"]
    F --> CORP["CyberCorp<br/>(the onchain entity)"]
    F --> IM["IssuanceManager"]
    F --> DM["DealManager<br/>(deals + secondary offers)"]
    F --> RM["RoundManager<br/>(fundraising rounds)"]
    IM -- "one LET contract<br/>per security series" --> LET["LedgerEntryToken<br/>(LET contracts, ERC-721 LETs)"]
    IM -. "optional: deployCyberScrip,<br/>at most one per LET contract" .-> SCRIP["CyberScrip<br/>(ERC-20 scrip)"]
    DM --- REG["CyberAgreementRegistry<br/>(templates + signed agreements)"]
    RM --- REG
    AUTH -. "authorizes" .-> IM
    AUTH -. "authorizes" .-> DM
    AUTH -. "authorizes" .-> RM
```

| Contract                                                                                               | Role                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [**CyberCorp**](/building-on-the-protocol/reference/contracts/cybercorp)                               | The onchain entity. Stores the company's name, type, jurisdiction, contact details, default dispute resolution, officers, escrowed officer signatures, an optional corp-level extension, and the addresses of its IssuanceManager, DealManager and RoundManager. UUPS-upgradeable.                   |
| [**CyberCorpFactory**](/building-on-the-protocol/reference/factories)                                  | Deploys a cyberCORP and its suite (BorgAuth, IssuanceManager, DealManager, RoundManager) in one call.                                                                                                                                                                                                |
| [**IssuanceManager**](/building-on-the-protocol/reference/contracts/issuancemanager)                   | The issuance authority. Creates LET contracts, mints and assigns Ledger Entry Tokens (LETs), registers security classes, deploys CyberScrip, runs scripification and de-scripification, manages recertification approvals, settles the ownership change of secondary trades, and voids emptied LETs. |
| [**LedgerEntryToken**](/building-on-the-protocol/reference/contracts/ledgerentrytoken)                 | The LET contract: an ERC-721 that mints the LETs of one security series. Tracks possession and the holder of record separately, with a transfer gate for each. Its IssuanceManager mutates it, and BorgAuth admins call its administrative functions directly.                                       |
| [**CyberScrip**](/building-on-the-protocol/reference/contracts/cyberscrip)                             | The ERC-20 scrip of one LET contract. USDC-style compliance powers (force transfer, force burn, freeze), each with a one-way disable switch.                                                                                                                                                         |
| [**CyberShares**](/building-on-the-protocol/reference/contracts/cybershares)                           | An ERC-20 share token with certificate-formation logic. Partly implemented; see its page.                                                                                                                                                                                                            |
| [**DealManager**](/building-on-the-protocol/reference/contracts/dealmanager)                           | Primary deals (propose, sign, finalize, void, revoke) and the secondary-trading venue (post, accept and cancel offers, settlement escrows, exemption pathways). Built on the agreement registry.                                                                                                     |
| [**RoundManager**](/building-on-the-protocol/reference/contracts/roundmanager)                         | Multi-investor fundraising rounds: create, submit EOIs, allocate, reject or recall, close.                                                                                                                                                                                                           |
| [**LeXscroWLite**](/building-on-the-protocol/reference/contracts/lexscrowlite)                         | The escrow used when deals and rounds close: the `LexScrowStorage` library that DealManager and RoundManager share.                                                                                                                                                                                  |
| [**CyberAgreementRegistry**](/building-on-the-protocol/reference/contracts/cyberagreementregistry)     | Onchain registry of agreement templates and executed multi-party agreements, with signing delegation and void-request tracking.                                                                                                                                                                      |
| [**SafeCertificateConverter**](/building-on-the-protocol/reference/contracts/safecertificateconverter) | Computes a SAFE-to-equity conversion plan from round data. A stub that returns an empty plan.                                                                                                                                                                                                        |
| [**LexChex / LeXcheXBadge**](/building-on-the-protocol/reference/contracts/lexchex)                    | ERC-5484 soulbound credentials: the LeXcheX accreditation NFT and the LeXcheXBadge fact-keyed credential registry.                                                                                                                                                                                   |
| [**CertificateUriBuilder**](/building-on-the-protocol/reference/contracts/certificateuribuilder)       | Builds the onchain JSON and SVG token URI of every LET, with the SVG drawn by a separate image-builder contract.                                                                                                                                                                                     |

[Factories](/building-on-the-protocol/reference/factories) covers the specialised factories (PumpCorp, MetaDAO, ParentCo).


# CyberCorp

The root contract of a cyberCORP: the onchain record of the legal entity.

* **Source:** [`src/CyberCorp.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberCorp.sol)
* **Inherits:** `Initializable`, `BorgAuthACL`, `UUPSUpgradeable`, `ICyberCorp`
* **`DEPLOY_VERSION`:** `"5"`; a company that has not upgraded reports `"3"` or `"4"`

## State

| Variable                         | Type               | Meaning                                                                 |
| -------------------------------- | ------------------ | ----------------------------------------------------------------------- |
| `cyberCORPName`                  | `string`           | Legal name, including designation ("Inc.", "LLC").                      |
| `cyberCORPType`                  | `string`           | Entity type as free-form text (e.g. "corporation"). **Not an enum.**    |
| `cyberCORPJurisdiction`          | `string`           | Jurisdiction of formation.                                              |
| `cyberCORPContactDetails`        | `string`           | Contact information.                                                    |
| `defaultDisputeResolution`       | `string`           | Default dispute-resolution mechanism for agreements.                    |
| `companyPayable`                 | `address`          | Address that receives payments on the company's behalf.                 |
| `issuanceManager`                | `address`          | The cyberCORP's IssuanceManager.                                        |
| `dealManager`                    | `address`          | The cyberCORP's DealManager.                                            |
| `roundManager`                   | `address`          | The cyberCORP's RoundManager.                                           |
| `upgradeFactory`                 | `address`          | Factory whose reference implementation gates upgrades.                  |
| `cyberCertPrinterImplementation` | `address`          | Unused. Holds its slot in the storage layout.                           |
| `companyOfficers`                | `CompanyOfficer[]` | Officers (`{eoa, name, contact, title}`).                               |
| `escrowedOfficerSignatures`      | `bytes[]`          | Reusable pre-authorized officer signatures.                             |
| `extension`                      | `address`          | Optional corp-level extension contract that interprets `extensionData`. |
| `extensionType`                  | `bytes32`          | Type selector for the active extension schema.                          |
| `extensionData`                  | `bytes`            | Raw extension payload interpreted by the active extension contract.     |

## Functions

```solidity
function initialize(address _auth, string _name, string _type, string _jurisdiction,
    string _contact, string _disputeResolution, address _issuanceManager,
    address _companyPayable, CompanyOfficer _officer, address _upgradeFactory,
    address _roundManager) external;

function setcyberCORPDetails(string _name, string _type, string _jurisdiction,
    string _contact, string _disputeResolution) external;        // onlyOwner
function setIssuanceManager(address) external;                    // onlyOwner
function setDealManager(address) external;                        // onlyOwner
function setRoundManager(address) external;                       // onlyOwner
function setCompanyPayable(address) external;                     // onlyOwner
function addOfficer(CompanyOfficer) external;                     // onlyOwner
function updateOfficer(uint256 index, CompanyOfficer) external;   // onlyOwner
function removeOfficer(address) external;                         // onlyOwner
function removeOfficerAt(uint256) external;                       // onlyOwner
function isCyberCORPOfficer(address) external view returns (bool);

function addEscrowedOfficerSignature(bytes signature) external;             // onlyRole(200)
function setEscrowedOfficerSignature(uint256 index, bytes signature) external; // onlyRole(200)
function getEscrowedOfficerSignature(uint256 index) external view returns (bytes);
function getEscrowedOfficerSignatureCount() external view returns (uint256);

function setExtension(address _extension, bytes32 _extensionType) external; // onlyOwner
function setExtensionData(bytes _extensionData) external;                   // onlyOwner
function clearExtension() external;                                         // onlyOwner
function getExtensionURI() external view returns (string);
```

## Officers and BorgAuth roles

* `addOfficer` reverts `DuplicateOfficer` for an `eoa` already listed. It grants the new officer role `200` unless the address already holds a higher custom role, which it leaves untouched.
* `updateOfficer` replaces the record at an index. When the `eoa` changes, it grants the new address under the same rule and revokes the old one, but only when the old role is exactly `200` and no other index still lists that `eoa` (an older company's officer list can hold duplicates).
* `removeOfficer` and `removeOfficerAt` revoke the role only when it is exactly `200` (a custom role granted outside the officer lifecycle survives) and the address is not listed at another index.
* `isCyberCORPOfficer` reports whether an address holds a role at or above `OWNER_ROLE` (99). See [Access control](/building-on-the-protocol/reference/access-control).

## Corp-level extension

`setExtension` installs an `ICyberCorpExtension` contract plus a schema selector. The contract must report `supportsExtensionType(_extensionType)`, or the call reverts `ExtensionTypeNotSupported`. Setting or replacing the extension clears any stored `extensionData`; `setExtensionData` then stores the payload, reverting `ExtensionNotConfigured` if no extension is set. `getExtensionURI` returns the extension-rendered JSON fragment for the current payload, and the [CertificateUriBuilder](/building-on-the-protocol/reference/contracts/certificateuribuilder) appends it to the metadata of every Ledger Entry Token (LET) the company issues.

## Events

`CyberCORPDetailsUpdated`, `OfficerAdded`, `OfficerUpdated`, `OfficerRemoved`, `CompanyPayableUpdated`, `EscrowedOfficerSignatureAdded`, `EscrowedOfficerSignatureUpdated`, `CyberCORPExtensionSet`, `CyberCORPExtensionDataUpdated`, and `IssuanceManagerUpdated` / `DealManagerUpdated` / `RoundManagerUpdated` (new address, old address), which the three manager setters emit.

## Errors

`NotRefImplementation`, `SignatureRequired`, `InvalidEscrowSignatureIndex`, `InvalidOfficerIndex`, `DuplicateOfficer`, `InvalidExtension`, `ExtensionTypeNotSupported`, `ExtensionNotConfigured`.

## Upgrades

`_authorizeUpgrade` is `onlyOwner` **and** requires the new implementation to equal `ICyberCorpSingleFactory(upgradeFactory).getRefImplementation()`; otherwise it reverts `NotRefImplementation`. The constructor calls `_disableInitializers()`, so the implementation contract itself can never be initialized.

This call upgrades the CyberCorp proxy only. Moving a company to v5 takes six calls: this one, `upgradeToAndCall` on the IssuanceManager, DealManager and RoundManager, and the IssuanceManager's two beacon upgrades. `corpUpgradeCalls` in `script/upgrade-v5.s.sol` builds the batch. A company left part-way through can see deal, round and issuance calls revert; see [Upgrade a cyberCORP](/building-on-the-protocol/how-to/upgrade-a-cybercorp).

This path needs the CyberCorp to sit behind its own ERC-1967 proxy. The oldest cyberCORPs are beacon proxies on MetaLeX-owned beacons, and for them `upgradeToAndCall` reverts `UUPSUnauthorizedCallContext`. See [Upgrade model](/building-on-the-protocol/reference/upgrade-model).


# IssuanceManager

The issuance authority: LET contracts, LETs, scrip, security classes and secondary transfers

The issuance authority for a cyberCORP. It creates LET contracts, issues and manages Ledger Entry Tokens (LETs), registers security classes, deploys CyberScrip, runs scripification, and settles the ownership change of secondary trades.

* **Source:** [`src/IssuanceManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/IssuanceManager.sol) / interface [`IIssuanceManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/IIssuanceManager.sol)
* **Pattern:** UUPS proxy that owns the `cyberCertPrinterBeacon` and `cyberScripBeacon` `UpgradeableBeacon`s. Most logic sits in the [`IssuanceManagerStorage`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/storage/IssuanceManagerStorage.sol) library to stay under the EIP-170 size limit.
* **`DEPLOY_VERSION`:** `"5"`; a company that has not upgraded reports `"4.1"` or `"3"`.

Each cyberCORP has one IssuanceManager. It creates **one LET contract per security series** (`createCertPrinter`) and **at most one CyberScrip per LET contract** (`deployCyberScrip`).

## LET lifecycle

```solidity
function createCertPrinter(string[] _ledger, string _name, string _ticker,
    string _certificateUri, SecurityClass _securityType,
    SecuritySeries _securitySeries, address _extension,
    bytes _seriesData) external returns (address);              // onlyOwner

function createCert(address certAddress, address to, CertificateDetails _details)
    external returns (uint256);                                  // onlyOwner
function createCertAndAssign(address certAddress, address investor,
    CertificateDetails _details) external returns (uint256 tokenId); // onlyOwnerOrSelf
function createCertAndAssignWithName(address certAddress, address investor,
    CertificateDetails _details, string investorName, bytes endorsementSignature,
    uint256 timestamp) external returns (uint256 tokenId);       // onlyOwnerOrSelf
function createCertSignAndAssign(address certAddress, address investor,
    CertificateDetails _details, bytes endorsementSignature, address registry,
    bytes32 agreementId, string investorName)
    external returns (uint256 tokenId);                          // onlyOwnerOrSelf
function assignCert(address certAddress, address from, uint256 tokenId,
    address investor, CertificateDetails _details,
    string investorName) external;                               // onlyOwner
```

The IssuanceManager has no `issueCert` function. All four `createCert*` functions mint a LET and register a holder of record; they differ in what else they write:

| Function                      | Writes                                                                                                                                                                                    |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createCert`                  | The bare mint. `to` becomes the holder of record, with a blank name, no endorsement and no signature. The deal and round managers use it to mint into escrow with `to` set to themselves. |
| `createCertAndAssign`         | Adds an issuance endorsement naming the investor, stamped with the current block time. No name on the register.                                                                           |
| `createCertAndAssignWithName` | Adds the holder's legal name and lets the caller set the endorsement date, so a reissue keeps the original date. The endorsement points at no agreement.                                  |
| `createCertSignAndAssign`     | Binds the endorsement to a `registry` and `agreementId`. Always stamped with the current block time.                                                                                      |

The three `*AndAssign` variants add a non-empty `endorsementSignature` as an issuer signature, and also attach the cyberCORP's first escrowed officer signature when one is stored.

`assignCert` mints nothing. It re-registers an existing LET to a new holder of record: `from` must be the current holder of record (not the wallet in possession), `investorName` is written to the register, and the call writes no endorsement and moves no payment. Use it to correct the register; trades settle through the DealManager. It passes through the LET contract's register gate (see [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken#delivery-and-registration-gates)).

`createCertPrinter` also files the new LET contract under its `SecurityClass` (see [Security-class registry](#security-class-registry)).

Signing, endorsement, voiding, legends, hooks and transferability live on the [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) contract itself, gated `onlyIssuanceManagerOrAdmin`, so BorgAuth admins call the LET contract directly.

```solidity
function voidEmptyCerts(address certAddress, uint256[] tokenIds) external; // onlyAdmin
```

`voidEmptyCerts` voids LETs that hold no units and no scrip-vault claim, so the LET contract's holder tally stops counting them. It skips LETs that are already void and reverts `CertNotEmpty` on a LET that still holds units or a claim. It is admin-only because another party can drive a LET's vault claim to zero while its holder still holds redeemable scrip.

## Security-class registry

Class-level designations (`SecurityClassInfo` in `CyberCorpConstants.sol`) are registered on the IssuanceManager. Each LET contract (the series scope) belongs to at most one class, and several LET contracts can share a class.

```solidity
function defineSecurityClass(SecurityClass _classType, string _documentURI,
    address _dataExtension, bytes _classData)
    external returns (uint256 classId);                          // onlyOwner
function updateSecurityClass(uint256 _classId, SecurityClass _classType,
    string _documentURI, address _dataExtension, bytes _classData) external; // onlyOwner
function setPrinterClass(address _printer, uint256 _classId) external;       // onlyOwner

function getSecurityClass(uint256 _classId) external view returns (SecurityClassInfo);
function getSecurityClassCount() external view returns (uint256);
function getPrinterClassId(address _printer) external view returns (uint256); // 0 = unclassified
```

Class ids are sequential from 1 (`0` = unclassified). Each `SecurityClass` value has one class: defining a second class of the same type reverts `SecurityClassAlreadyDefined`. `createCertPrinter` assigns the new LET contract to its type's class, creating an empty one (no document URI, no data) when none exists, and `updateSecurityClass` fills it in later. `setPrinterClass(printer, 0)` clears an assignment, and `setPrinterClass` also gives a class to a LET contract that has none, such as one carried over from an IssuanceManager without the registry.

The class's `classData` is the class scope of a LET: [`ShareExtensionV3`](/building-on-the-protocol/reference/extensions#shareextension--shareextensionv3) reads it as the least specific layer of every LET in a share class.

## Secondary transfers

```solidity
function secondaryTransfer(bytes dealMetadata) external;         // onlyOwner
```

Settles the ownership change of a secondary trade. Gated on `OWNER_ROLE`, which the SPV's [DealManager](/building-on-the-protocol/reference/contracts/dealmanager) holds; `dealMetadata` is the ABI-encoded tuple that `DealManager.finalizeSecondaryTradeAgreement` produces. The seller's LET never moves wallets. The trade mutates the seller's LET and mints a new one for the buyer:

1. The seller's endorsement (signed in blank by the seller, now naming the buyer) is written onto the seller's LET.
2. The sold units are deducted from the seller's LET.
3. The buyer gets a new LET through `LedgerEntryToken.safeMintFromAndAssign`, which names the seller's LET as the source. The LET contract treats that as a change of holder from the seller, so its stop-transfer flag, its registration hooks and its void check all apply. Under administered hosting the token goes to the admin multisig while the buyer is registered as holder of record.
4. The seller's LET is voided only if it holds no units **and** no scrip-vault claim. Otherwise it stays live, and `voidEmptyCerts` can sweep it later.
5. The same endorsement is copied onto the buyer's LET, and `SecondaryTransferExecuted` is emitted.

A new LET contract's register is closed by default. An admin must open it (`LedgerEntryToken.setGlobalLegalTransferable(true)` or the per-LET switch) before secondary trades on that LET contract can settle.

## Scripification

```solidity
function deployCyberScrip(address certAddress,
    ITransferRestrictionHook[] typeRestrictionHooks,
    ICondition[] certToScripConditions, ICondition[] scripToCertConditions,
    uint256 scripToCertMinimum, uint256 scripRatioNumerator,
    uint256 scripRatioDenominator, uint256[] scripifyWhitelistIds,
    bool scripifyWhitelistEnabled, bool enableForceTransfer,
    bool enableForceBurn, bool enableFreeze) external returns (address); // onlyOwner

function scripifyCert(address certAddress, uint256 id, uint256 amount, address target) external;
function convertScripToCert(address certAddress, uint256 amount) external;
function setScripRatio(address certAddress, uint256 numerator, uint256 denominator) external; // onlyOwner
function setScripToCertMinimum(address certAddress, uint256 minimum) external;                // onlyOwner

function setRecertificationApproval(address certAddress, address investor,
    string investorName, CertificateDetails details, bytes officerSignature) external; // onlyAdmin
function clearRecertificationApproval(address certAddress, address investor) external; // onlyAdmin
```

The LET's holder of record calls `scripifyCert`, which converts only units not reserved for a pending deal. When the scrip's freeze power is enabled, a frozen account can neither scripify nor convert scrip back (`AccountFrozen`). Conversion burns scrip outside CyberScrip's own freeze check, so the IssuanceManager makes that check itself.

`convertScripToCert` runs the LET contract's scrip-to-LET conditions. A converter who is not the holder of record of a live LET in that LET contract also needs a recertification approval from an admin (`setRecertificationApproval`, carrying an officer signature); without one the call reverts `RecertificationApprovalRequired`.

Scripified units sit in a vault per LET contract. Converting scrip back first draws on the backing attributed to the converter's own non-void LETs (in the order they are registered to the converter); any excess, and every force burn, reduces all remaining LETs' claims in proportion. Moving scrip between wallets does not move that attribution.

Scripify-whitelist management: `setScripifyWhitelistEnabled`, `addScripifyWhitelistIds`, `removeScripifyWhitelistIds`, `isScripifyWhitelisted`, `getScripifyWhitelistEnabled`. Views: `getScripRatio`, `getScripToCertMinimum`, `getRecertificationApproval`, `getCertScripifiedStatus`, `getScripPoolTotals`, `getCertScripUnitVault`, `getScripPoolAmountById`, `getScripPoolSharesById`. `getScripPoolTotals` returns the scrip `totalSupply` and the vault price per nominal share in ray, which is `1e27` for any initialized, non-empty pool. Per-LET claims are floored, so their sum can fall short of the pool total by rounding dust.

## Scrip compliance administration

A BorgAuth admin calls most CyberScrip compliance controls on the scrip itself (see [CyberScrip](/building-on-the-protocol/reference/contracts/cyberscrip)). Force burn goes through the IssuanceManager, because it also withdraws the matching backing from the vault:

```solidity
function forceScripBurn(address certAddress, address account, uint256 amount) external; // onlyAdmin
```

## Beacons and configuration

`CORP()`, `uriBuilder()` / `setUriBuilder`, `companyName()`, `companyJurisdiction()`, `AUTH()`, `DEPLOY_VERSION()`, `printers(index)`, `isPrinter(address)`, `cyberCertPrinterBeacon()`, `cyberScripBeacon()`, `getCertPrinterBeaconImplementation()`, `getScripBeaconImplementation()`, `getUpgradeFactory()`, `upgradeCertPrinterBeaconImplementation`, `upgradeScripBeaconImplementation`. Beacon upgrades are `onlyOwner` and accept only the factory's current reference implementation (`NotRefImplementation` otherwise). `setUriBuilder` emits `UriBuilderUpdated(new, old)`.

## Events

`CertPrinterCreated`, `CertificateCreated`, `CyberScripDeployed`, `SecurityClassDefined`, `SecurityClassUpdated`, `PrinterClassAssigned`, `SecondaryTransferExecuted`, `ScripifiedCert`, `ScripRecertified`, `ScripAddedToExistingCert`, `ScripToCertMinimumSet`, `ScripifyWhitelistEnabledSet`, `ScripifyWhitelistUpdated`, `RecertificationApprovalSet`, `RecertificationApprovalCleared`, `CertPrinterBeaconImplementationUpgraded`, `ScripBeaconImplementationUpgraded`, `UriBuilderUpdated`.

`CertificateCreated(uint256 indexed tokenId, address indexed certificate, uint256 amount, uint256 cap, CertificateDetails details)` does not carry the token URI; indexers read `tokenURI(tokenId)` from the LET contract.

## Access

State-changing functions are gated through BorgAuth:

* `onlyOwner` (role 99 and above): issuance and configuration.
* `onlyAdmin` (role 98 and above): `forceScripBurn`, `voidEmptyCerts` and recertification approvals.
* `onlyOwnerOrSelf` (the contract itself or owner-role callers): the `createCert*AndAssign` variants.

The deal and round managers hold owner-level roles, which is how their flows mint LETs. The source has the exact role each function requires.


# LedgerEntryToken

The LET contract, the ERC-721 that mints and registers a series' Ledger Entry Tokens (LETs)

`LedgerEntryToken` is the LET contract: an ERC-721 whose tokens, Ledger Entry Tokens (LETs), are one security series' entries on a cyberCORP's register of holders. Each class or series has its own LET contract, which the company's [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager) creates with `createCertPrinter`.

* **Source:** [`src/LedgerEntryToken.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/LedgerEntryToken.sol) / interface [`ILedgerEntryToken.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILedgerEntryToken.sol) / storage library [`LedgerEntryTokenStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/storage/LedgerEntryTokenStorage.sol)
* **Inherits:** `ERC721EnumerableUpgradeable`, `ILedgerEntryToken`
* **Pattern:** beacon proxy; the IssuanceManager owns the beacon
* **Storage position:** `keccak256("cybercorp.cert.printer.storage.v1")`
* **`DEPLOY_VERSION`:** `"5"`; the LET contracts of a company that has not upgraded report `"4"` or `"3"`. Some external signatures differ between v4 and v5 (the v5 `assignCert` takes a name argument, for example), so pick the ABI by the LET contract's version.

Two auth modifiers gate state changes:

* `onlyIssuanceManager`: minting, assignment, details updates, `setExtension` and `updateIssuanceManager` accept only the IssuanceManager that deployed the LET contract.
* `onlyIssuanceManagerOrAdmin`: the administrative functions (legends, hooks, both kinds of transferability switch, voiding, signatures, endorsements, timestamps, reserved units, series data, the look-through badge, `initializeHolderCount`) also accept callers holding BorgAuth `ADMIN_ROLE` or higher, so admins call them directly.

## Minting and assignment

```solidity
function safeMint(uint256 tokenId, address to, CertificateDetails details)
    external onlyIssuanceManager returns (uint256);
function safeMintAndAssign(address to, uint256 tokenId, CertificateDetails details,
    string investorName) external onlyIssuanceManager returns (uint256);
function safeMintAndAssign(address to, address owner, uint256 tokenId,
    CertificateDetails details, string ownerName)
    external onlyIssuanceManager returns (uint256); // custodian ≠ legal owner
function safeMintFromAndAssign(uint256 sourceTokenId, address to, address owner,
    uint256 tokenId, CertificateDetails details, string ownerName)
    external onlyIssuanceManager returns (uint256); // secondary-trade reissue
function assignCert(address from, uint256 tokenId, address to,
    CertificateDetails details, string name) external onlyIssuanceManager returns (uint256);
function updateCertificateDetails(uint256 tokenId, CertificateDetails details)
    external onlyIssuanceManager;
```

The second `safeMintAndAssign` overload separates the custodian (`to`, who holds the token) from the legal owner (`owner`, the holder of record). It supports **administered hosting**, where the token goes to an admin multisig while the buyer is registered as owner. Both overloads record the details and the holder of record before the ERC-721 receiver callback runs.

`safeMintFromAndAssign` mints the buyer's LET in a secondary trade and names the seller's LET it came from. A reissue moves title but is technically a mint; naming the source lets the register gate treat it as a transfer from the seller. It reverts `VoidCertificate` if the source LET is void.

`assignCert` re-registers an existing LET: `from` must be the current holder of record (`legalOwnerOf`, not the wallet in possession), and `name` is the incoming holder's legal name for the register. `assignCert` and `updateCertificateDetails` enforce the reserved-units invariant: while units are escrowed for a pending deal, legal ownership cannot be reassigned and `unitsRepresented` cannot drop below `unitsReserved` (`CertificateReserved` / `ExceedsAvailableUnits`). `updateCertificateDetails` emits `CertificateDetailsUpdated(tokenId)`.

## Delivery and registration gates

The LET contract gates two different events separately:

| Event                                              | Flags (both default `false`)                                                            | Hook call                       | Failure                                            |
| -------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------- | -------------------------------------------------- |
| **Delivery** (the token moves between two wallets) | `transferable` (`setGlobalTransferable`), per-LET `setTokenTransferable`                | `checkTransferRestriction`      | `TokenNotTransferable` / `TransferRestricted`      |
| **Registration** (the holder of record changes)    | `legalTransferable` (`setGlobalLegalTransferable`), per-LET `setTokenLegalTransferable` | `checkLegalTransferRestriction` | `LegalOwnerNotTransferable` / `TransferRestricted` |

For each event, either the contract-wide flag or the LET's own flag permits it. Both kinds of check use the contract's `globalRestrictionHook` and the LET's own hook (see [Hooks](/building-on-the-protocol/reference/hooks)).

* Original issuance is never gated: a mint has no outgoing holder.
* The company's DealManager and RoundManager are exempt from both flags when they are the outgoing party, so they can deliver primary issuances out of escrow. Hooks still run for them.
* A void LET cannot be registered to a new holder (`VoidCertificate`).
* A LET with reserved units cannot be transferred or reassigned.

A new LET contract issues freely, but holders cannot move its LETs and its register cannot change until an admin opens each gate. On a LET contract upgraded from v4 the registration flags also read `false`: after the upgrade, `assignCert`, endorsed transfers and secondary-trade settlement on it revert `LegalOwnerNotTransferable` until an admin opens the register.

Views: `transferable()`, `isTokenTransferable(id)`, `legalTransferable()`, `isTokenLegalTransferable(id)`. Events: `GlobalTransferableSet`, `TokenTransferableSet`, `GlobalLegalTransferableSet`, `TokenLegalTransferableSet`.

## Endorsements and signatures

```solidity
function addEndorsement(uint256 tokenId, Endorsement newEndorsement) public;
function endorseCertificate(uint256 tokenId, address endorser, bytes signature,
    bytes32 agreementId) external onlyIssuanceManagerOrAdmin;
function endorseAndTransfer(uint256 tokenId, Endorsement e, address from, address to) external;
function addIssuerSignature(uint256 tokenId, bytes signature) external onlyIssuanceManagerOrAdmin;
function getEndorsementHistory(uint256 tokenId, uint256 index) external view returns (Endorsement);
function getIssuerSignatureCount(uint256 tokenId) external view returns (uint256);
function getIssuerSignatureAt(uint256 tokenId, uint256 index) external view returns (bytes);
```

`addEndorsement` accepts only the IssuanceManager or the token's **legal owner**. Possession alone does not authorize an endorsement; if it did, a custodian could endorse a LET to itself and take legal title on delivery.

`endorseAndTransfer` records the endorsement and then moves the token as `msg.sender`, so moving a token held by someone else needs that holder's ERC-721 approval. The holder of record cannot take back possession that a custodian or pledgee holds.

## Void and status

`voidCert` and `unvoidCert` (both `onlyIssuanceManagerOrAdmin`), `isVoided`. `voidCert` reverts `TokenDoesNotExist` for an unminted id. Voiding leaves the legal-owner enumeration as it is, but a fully voided LET stops counting its owner in the look-through holder tally. The IssuanceManager's `voidEmptyCerts` voids LETs that hold no units and no scrip-vault claim.

## Legends, hooks and extension data

Plain-string legends: `addDefaultLegend` / `removeDefaultLegendAt` / `getDefaultLegendAt` / `getDefaultLegendCount`, and per LET `addCertLegend` / `removeCertLegendAt` / `getCertLegendAt` / `getCertLegendCount`.

Structured **restrictive legends** (`RestrictiveLegend{restrictionType, title, text, jurisdiction, referenceId, effectiveTimestamp, expirationTimestamp, active, data}`): `addDefaultRestrictiveLegend` / `removeDefaultRestrictiveLegendAt` / `getDefaultRestrictiveLegendAt` / `getDefaultRestrictiveLegendCount`, and per LET `addCertRestrictiveLegend` / `removeCertRestrictiveLegendAt` / `getCertRestrictiveLegendAt` / `getCertRestrictiveLegendCount`. Each add or remove emits `DefaultLegendsChanged()` or `CertLegendsChanged(tokenId)`, and `tokenURI` reads the legends live.

Hooks: `setRestrictionHook(id, hook)` and `setGlobalRestrictionHook`. The transferability switches are under [Delivery and registration gates](#delivery-and-registration-gates).

Extension data: `setExtension` (`onlyIssuanceManager`; the extension applies to the whole LET contract, the `tokenId` argument is ignored, and the call emits `SeriesExtensionSet`), `getExtension` and `getExtensionData`, plus series-scope data through `setSeriesData` (`onlyIssuanceManagerOrAdmin`) and `getSeriesInfo`. The LET contract's single extension decodes both the per-LET and the series payloads (see [Certificate extensions](/building-on-the-protocol/reference/extensions)).

## Reserved units and timestamps

```solidity
function increaseUnitsReserved(uint256 tokenId, uint256 amount) external; // onlyIssuanceManagerOrAdmin
function decreaseUnitsReserved(uint256 tokenId, uint256 amount) external; // onlyIssuanceManagerOrAdmin
function unitsReserved(uint256 tokenId) external view returns (uint256);

function issueTimestamp(uint256 tokenId) external view returns (uint64);
function setIssueTimestamp(uint256 tokenId, uint64 ts) external;          // onlyIssuanceManagerOrAdmin
function acquisitionTimestamp(uint256 tokenId) external view returns (uint64);
function setAcquisitionTimestamp(uint256 tokenId, uint64 ts) external;    // onlyIssuanceManagerOrAdmin
function updateCertificateTackedFromAcquisitionDate(uint256 tokenId, uint64 ts) external; // onlyIssuanceManagerOrAdmin
function backfillAcquisitionTimestamps(uint256 startIndex, uint256 count) external; // permissionless
```

Reserved units escrow part of a LET against a pending deal; a LET with `unitsReserved > 0` cannot be transferred or reassigned.

`issueTimestamp` is stamped at mint, and `acquisitionTimestamp` is stamped again on each legal-owner change. Admins can override both for migrated positions. `updateCertificateTackedFromAcquisitionDate` overrides a LET's Rule 144(d)(3) tacking anchor inside its `FundInterestExtensionV3` data. `backfillAcquisitionTimestamps` copies that extension's `acquisitionDate` into the base field of every LET whose base field is unset; on a LET contract without the fund-interest extension it does nothing.

## Token possession and registered ownership

The LET contract tracks two owners:

* `ownerOf(tokenId)`: the ERC-721 token holder (possession or custody).
* `legalOwnerOf(tokenId)`: the **registered owner of record**, stored separately in the LET's `OwnerDetails`.

The `_update` override keeps them apart. A transfer updates the registered owner only when it delivers the token to the endorsee of the latest live **endorsement** (or when `endorsementRequired` is false, which only a test harness sets), and then only if the registration gate allows it. Moving the token without an endorsement leaves the registered owner unchanged. Every change of holder of record retires the endorsements written before it: only an endorsement added since the last change can move title, so a former owner cannot replay an old one. [LETs and scrip](/how-cybercorps-works/lets-and-scrip) explains the model this implements.

The register is also enumerable by legal owner:

```solidity
function balanceOfLegalOwner(address owner) external view returns (uint256);
function tokenOfLegalOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function isLegalHolder(address owner) external view returns (bool); // holds ≥1 live (non-void) LET
function backfillLegalOwners(uint256 startIndex, uint256 count) external; // permissionless, idempotent
```

On a LET contract upgraded from a version without this enumeration, call `backfillLegalOwners` in batches over `[0, totalSupply())`.

## Holder counts

`holderCount()` counts distinct wallets that **possess** a LET of the series, updated on delivery. A LET contract upgraded from a version without this per-wallet counter can hold wallets whose counter reads zero, and a transfer out of such a wallet reverts. `initializeHolderCount(holder)` (`onlyIssuanceManagerOrAdmin`) seeds the counter from the wallet's current balance and reverts `HolderCountAlreadyInitialized` if it is already nonzero. Seed every such holder before anything is minted or transferred to or from it; a counter that has already moved off zero cannot be repaired this way.

For Investment Company Act §3(c)(1)(A) accounting, the LET contract also keeps an incrementally updated **look-through** tally of holders of record, reading beneficial-owner counts and residency from a configured [LeXcheXBadge](/building-on-the-protocol/reference/contracts/lexchex):

```solidity
function lookThroughHolderCount() external view returns (uint256);   // Σ max(beneficialOwnerCount, 1)
function usLookThroughHolderCount() external view returns (uint256); // U.S.-resident subset
function usTallyExpiry() external view returns (uint64);             // when the U.S. subtotal stops being trusted
function lookThroughBadge() external view returns (address);
function setLookThroughBadge(address badge) external;                // onlyIssuanceManagerOrAdmin
function resyncHolder(address owner) external;                       // permissionless
function resyncHolders(address[] owners) external;
function backfillLookThroughTally(uint256 startIndex, uint256 count) external;
```

Past `usTallyExpiry` the U.S. subtotal conservatively reports the full look-through count, because a non-US booking may have lapsed into US unobserved; keepers call `resyncHolder` before that time passes. Set the badge before the first mint on a new LET contract. `backfillLookThroughTally` is idempotent and re-reads every holder from the badge, so running it again after wiring a badge late repairs holders that were booked without one.

## Views

`tokenURI`, `getCertificateDetails` (reports `unitsRepresented` **plus** scripified units), `getActiveCertificateDetails` (raw `unitsRepresented`), `defaultLegend`, `defaultRestrictiveLegends`, `certificateUri`, `issuanceManager`, `securityType` (`SecurityClass`), `securitySeries` (`SecuritySeries`), `transferable`, `legalTransferable`, `endorsementRequired`, `legalOwnerOf`.

## Events

`CyberCertPrinter_CertificateCreated`, `CertificateAssigned`, `CertificateEndorsed`, `CertificateSigned`, `CertificateVoided`, `CertificateUnvoided`, `CertificateDetailsUpdated`, `CyberCertTransfer`, `LegalOwnerChanged`, `RestrictionHookSet`, `GlobalRestrictionHookSet`, `GlobalTransferableSet`, `TokenTransferableSet`, `GlobalLegalTransferableSet`, `TokenLegalTransferableSet`, `DefaultLegendsChanged`, `CertLegendsChanged`, `LookThroughBadgeSet`, `UnitsReservedUpdated`, `IssueTimestampSet`, `AcquisitionTimestampSet`, `SeriesDataSet`, `SeriesExtensionSet`, `IssuanceManagerUpdated`, `HolderCountInitialized`.

`CertificateCreated`, `Converted`, `HookStatusChanged` and `WhitelistUpdated` are declared in `ILedgerEntryToken` but never emitted.


# CyberScrip

A fungible ERC-20 token that tracks the units of one LET contract's Ledger Entry Tokens (LETs) in a fixed ratio (`setScripRatio` on the IssuanceManager). Holders convert LET units into scrip and back through the IssuanceManager. The IssuanceManager deploys the scrip with `deployCyberScrip`, at most one per [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) contract. Scrip's rights come from the company's governing documents and the scrip's terms; under MetaLeX-form bylaws scrip is not stock and confers no stockholder rights on its own.

* **Source:** [`src/CyberScrip.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberScrip.sol) / interface [`ICyberScrip.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ICyberScrip.sol)
* **Inherits:** `ERC20Upgradeable`, `BorgAuthACL`, `ICyberScrip`
* **Pattern:** beacon proxy; the IssuanceManager owns the beacon
* **`DEPLOY_VERSION`:** `"5"`; the scrip of a company that has not upgraded reports `"4"` or lower

The IssuanceManager drives scripification and de-scripification, so `mint` and `burnFrom` are `onlyIssuanceManager`. The compliance controls other than force burn are `onlyIssuanceManagerOrAdmin`, so a cyberCORP admin (BorgAuth level 98 or above) calls them on the scrip directly.

## Mint and burn

```solidity
function mint(address to, uint256 amount) external onlyIssuanceManager;
function burnFrom(address account, uint256 amount) external onlyIssuanceManager;
```

## Compliance powers

CyberScrip has **three** compliance powers and **no blocklist**.

| Power          | Function                          | Gate             | Enabled at deploy by  | Disable (one-way)        |
| -------------- | --------------------------------- | ---------------- | --------------------- | ------------------------ |
| Force transfer | `forceTransfer(from, to, amount)` | manager or admin | `enableForceTransfer` | `disableForceTransfer()` |
| Force burn     | `forceBurn(account, amount)`      | manager only     | `enableForceBurn`     | `disableForceBurn()`     |
| Freeze         | `setFrozen(account, isFrozen)`    | manager or admin | `enableFreeze`        | `disableFreeze()`        |

Each `disable*` function is `onlyIssuanceManagerOrAdmin` and irreversible: a disabled power cannot be re-enabled. A power works only while its `can*` flag is true; otherwise the call reverts `ComplianceFeatureDisabled`.

Force burn is manager-only because it must also withdraw the matching backing units from the LET contract's scrip vault. An admin exercises it through `IssuanceManager.forceScripBurn` (see [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager)). Force transfer and force burn bypass the transfer hooks and the freeze.

A frozen account cannot send or receive scrip. While freezing is enabled, the IssuanceManager also refuses to let a frozen account scripify a LET or convert scrip back into a LET (`AccountFrozen`).

## Transfer restrictions

`setRestrictionHook(ITransferRestrictionHook[])` (`onlyIssuanceManagerOrAdmin`) replaces the whole array of [transfer hooks](/building-on-the-protocol/reference/hooks). Every transfer **and mint** (any move to a nonzero address) runs each hook's `checkTransferRestriction`, and a `false` result reverts `RestrictedTransfer(reason)`. Burns skip the hooks. Frozen accounts (when `canFreeze`) revert `AccountFrozen`.

## Holder cap

`setMaxHolderCount(uint256)` (`onlyIssuanceManagerOrAdmin`) sets a maximum holder count (`0` = unlimited, the default) and emits `MaxHolderCountUpdated`. A transfer that would exceed it reverts `HolderLimitExceeded`.

On a v4 company the holder cap cannot be set: the v4 scrip's setter is `onlyIssuanceManager`, and the v4 IssuanceManager has no function that calls it.

## Views

`certPrinter`, `issuanceManager`, `transferRestrictionHooks(i)`, `transferRestrictionHooksLength`, `canForceTransfer`, `canForceBurn`, `canFreeze`, `frozen(account)`, `holderCount` / `currentHolderCount`, `maxHolderCount`, `remainingSlots`, `canTransfer(from, to, amount)`, `willCreateNewHolder(to, amount)`.

## Events

`ForceTransfer`, `ForceBurn`, `FreezeStatusUpdated`, `ComplianceFeatureDisabledEvent`, `MaxHolderCountUpdated`.

## Errors

`RestrictedTransfer`, `NotIssuanceManager`, `ComplianceFeatureDisabled`, `AccountFrozen`, `HolderLimitExceeded`.


# CyberShares

* **Source:** [`src/CyberShares.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberShares.sol)
* **Inherits:** `ERC20Upgradeable`

> **Partly implemented.** Several functions, including `safeMint` and `certificateTokenURI`, have their bodies commented out or return empty values. This page describes the intended shape; check the source before relying on it.

## Purpose

`CyberShares` is an ERC-20 share token for a single security class that also carries certificate-formation logic: fungible shares can be formed into individual certificates and voided back into shares.

## Functions

```solidity
function mint(address to, uint256 amount) external onlyIssuanceManager;
function burn(address from, uint256 amount) external onlyIssuanceManager;
function voidToShares(uint256 certId) external;          // burn a certificate back to shares
function formCertificateFromShares(uint256 amount, CertificateDetails details) external;
function setGlobalRestrictionHook(address hookAddress) external onlyIssuanceManager;
function setTransferable(bool _transferable) external onlyIssuanceManager;
function setCertThreshold(uint256 _threshold) external onlyIssuanceManager;
function decimals() public view returns (uint8);
function certThreshold() public view returns (uint256);
function sharesUri() public view returns (string);
```

It also exposes `certificate*` helpers (`certificateOwnerOf`, `certificateApprove`, `certificateTransferFrom` and others) that mirror an ERC-721 surface for the formed certificates.

## Events

`SharesMinted`, `SharesBurned`, `CertificateFormed`, `SharesFromCertificate`, `SharesFromVoidedCertificate`, `GlobalRestrictionHookSet`, `TransferableSet`, `CertThresholdSet`, plus `Certificate*` events.

## Share counts

`CyberShares` keeps no `authorized`, `outstanding` or `reserved` counters. The register of holders is the set of Ledger Entry Tokens (LETs) on the company's [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) contracts, and share counts are read from there.


# DealManager

Primary deals and the secondary-trading venue with settlement escrow

Runs a cyberCORP's deals: primary issuance deals built on the agreement registry, and a secondary-trading venue with its own offer and settlement machinery. A deal is created from an agreement template and identified by a `bytes32 agreementId`.

* **Source:** [`src/DealManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/DealManager.sol) / interface [`IDealManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/IDealManager.sol)
* **Pattern:** UUPS proxy. The `DealManagerStorage`, `SecondaryTradeStorage` and `LexScrowStorage` libraries hold most of the logic. The deal and secondary-trade events and errors are declared in the `IDealManagerStorage` and `ISecondaryTradeStorage` interfaces so they appear in DealManager's ABI. `ILexScrowStorage` contributes the shared `AgreementConditionsNotMet` error and the escrow views, and the escrow events (`DealPaidAt` and the others) are declared in the `LexScrowStorage` library itself. All of them are emitted from the DealManager's address.
* **`DEPLOY_VERSION`:** `"5"`. The DealManager of a company that has not upgraded reports `"4.0.1"` or lower; read `DEPLOY_VERSION()` on the instance before choosing an ABI (see [Integrate from a frontend](/building-on-the-protocol/how-to/integrate-from-frontend)).

## Primary deal lifecycle

```solidity
function proposeDeal(address[] _certPrinterAddress, address _paymentToken,
    uint256 _paymentAmount, bytes32 _templateId, uint256 _salt,
    string[] _globalValues, address[] _parties, CertificateDetails[] _certDetails,
    string[][] _partyValues, address[] conditions, bytes32 secretHash,
    uint256 expiry) external
    returns (bytes32 agreementId, uint256[] certIds);            // onlyOwner

function proposeAndSignDeal(/* ...as above, plus */ address proposer,
    bytes signature /* ... */) external
    returns (bytes32 agreementId, uint256[] certIds);            // onlyOwner

function proposeAndSignNewCertsDeal(uint256 salt, CyberCertData[] _certData,
    bytes32 _templateId, string[] _globalValues, address[] _parties,
    uint256 _paymentAmount, string[][] _partyValues, bytes signature,
    CertificateDetails[] _details, address[] conditions, bytes32 secretHash,
    uint256 expiry, address stableAddress) external
    returns (address[] certPrinterAddress, bytes32 id, uint256[] certIds); // onlyOwner

function signDealAndPay(address signer, bytes32 agreementId, bytes signature,
    string[] partyValues, bool _fillUnallocated, string name, string secret) external;
function signAndFinalizeDeal(address signer, bytes32 agreementId,
    string[] partyValues, bytes signature, bool _fillUnallocated,
    string name, string secret) external;
function finalizeDeal(bytes32 agreementId) external;

function voidExpiredDeal(bytes32 _agreementId, address signer, bytes signature) external;
function revokeDeal(bytes32 _agreementId, address signer, bytes signature) external;
function signToVoid(bytes32 _agreementId, address signer, bytes signature) external;
function refundVoidedDeal(bytes32 agreementId) external; // for deals voided directly in the registry

function addCondition(bytes32 agreementId, address condition) external;      // onlyOwner, pending deals only
function removeConditionAt(bytes32 agreementId, uint256 index) external;     // onlyOwner, pending deals only

function initialize(address _auth, address _corp, address _dealRegistry,
    address _issuanceManager, address _upgradeFactory) external;
```

`proposeAndSignNewCertsDeal` deploys new LET contracts (through `IssuanceManager.createCertPrinter`, prefixing the company name) and proposes and signs the deal in one transaction. `CyberCertData` carries `{name, symbol, uri, securityClass, securitySeries, extension, seriesData, defaultLegend}`; it is declared once in `src/CyberCorpConstants.sol` and shared by the deal path, the round path and the corporate factories. A v4 DealManager takes the struct without `seriesData`, so the function selector differs between v4 and v5.

`proposeDeal` creates the registry agreement with the DealManager as its `finalizer`, so only the DealManager can finalize it and the registry accepts a relayed signature only when the DealManager submits it. Parties sign the registry's `SignatureData` with `signer` set to their own address (see [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry#data-model)).

## How primary deals work

* A deal references an agreement **template** (`_templateId`) and is recorded in the [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry), passed to `initialize` as `_dealRegistry`.
* `_parties` countersign with EIP-712 signatures; `signDealAndPay` combines a party's signature with their payment.
* `conditions` are `ICondition` addresses that gate the deal. The owner can `addCondition` / `removeConditionAt` while the deal is pending.
* `expiry` and `secretHash` support timed and secret-gated deals, and `voidExpiredDeal` cleans up expired ones. `expiry == 0` means **no deadline**: such a deal can pay and finalize at any time and can never be voided as expired (`voidExpiredDeal` reverts `DealNotExpired`), so it unwinds only by void request (see [Voiding a primary deal](#voiding-a-primary-deal)).
* `finalizeDeal` applies the deal's LET effects (mint, assign, endorse) through the IssuanceManager and releases the escrowed payment, less the primary platform fee (`computeFee` / `getPlatformPayable`).
* Escrow state lives in the shared `LexScrowStorage` library (see [LeXscroWLite](/building-on-the-protocol/reference/contracts/lexscrowlite)). `getEscrowDetails(agreementId)` and `conditionCheck(agreementId)` expose it.

### Voiding a primary deal

Every void runs through the registry's `voidContractFor`. It voids the agreement once every **allocated** party has requested it, as soon as the proposer (party index 0) requests while still the only signer, or, for a nonzero expiry only, once that expiry has passed. The registry knows nothing of the escrow, so the DealManager entry points add the teardown: they void the company's Ledger Entry Tokens (LETs) minted into escrow at proposal and refund or close the escrow.

* `signToVoid` forwards the request. Once the registry reports the agreement voided, it voids the escrowed LETs and settles the escrow: a `PAID` escrow is refunded and a `PENDING` one is marked `VOIDED`. Until then it only records the request.
* `revokeDeal` is the same route for a deal that has not been paid. It reverts `DealNotPending` unless the escrow is `PENDING` and forwards the request. When that request voids the agreement (the sole-signer proposer's request, for example), it voids the escrowed LETs and marks the escrow `VOIDED`.
* Both require the caller to be the `signer` they name (`CounterPartyValueMismatch` otherwise).
* `voidExpiredDeal` applies only to a deal whose nonzero expiry has passed. It forwards the request, voids the LETs, and refunds or closes the escrow.
* Requests can also go straight to the registry, bypassing the DealManager. The escrow then lags the agreement until `refundVoidedDeal` syncs it, which voids the company's LETs and refunds, for a `PAID` escrow only. On a `PENDING` escrow `voidAndRefund` reverts `EscrowNotPaid`, so an unpaid deal voided this way still needs `signToVoid` from a party that has not yet requested the void.

## Secondary trading

DealManager is also the SPV-side secondary-trading venue for LETs: sell and buy offers, partial acceptances, settlement escrows, and layered compliance conditions.

```solidity
function postOffer(PostOfferParams params) external returns (bytes32 offerId);
function acceptOffer(AcceptOfferParams params) external returns (bytes32 settlementAgreementId);
function cancelOffer(bytes32 offerId) external;
function finalizeSecondaryTradeAgreement(bytes32 agreementId) external;
function voidSecondaryTradeAgreement(bytes32 agreementId, address signer, bytes signature) external;
function voidSecondaryTradeAgreement(bytes32 agreementId, address signer, bytes signature, uint256 nonce, bytes authSig) external;
function voidExpiredSecondaryTradeAgreement(bytes32 agreementId, address signer, bytes signature) external;
function syncVoidedSecondaryTradeAgreement(bytes32 agreementId) external;
```

`postOffer`, `acceptOffer` and `cancelOffer` each have a relayer overload taking `(…, address forAddr, uint256 nonce, bytes sig)`, where `sig` is the user's EIP-712 authorization, so a relayer can submit on a user's behalf. The relayed `voidSecondaryTradeAgreement` overload has its own shape, `(agreementId, signer, voidSignature, nonce, authSig)`: the registry void signature and the relayer-authorization signature are separate, with `signer` as the authorized party.

* **Offers.** `PostOfferParams` covers both sides (`OfferSide.SELL` / `BUY`): the LET contract and token id, units, payment token and consideration, validity window, an agreement template with party values and the offeror's signature, an open endorsement signature (sell side), and the buyer's hosting mode. Offer status runs `LIVE → PARTIALLY_ACCEPTED / FULLY_ACCEPTED → FINALIZED`, or `CANCELLED`.
* **Acceptance and settlement.** `acceptOffer` fills an offer fully or partially: it reserves the seller's LET units, escrows the buyer's consideration and creates a settlement agreement. Each fill is priced from the offer's running total. The fill pays `offer.consideration × (offer.unitsAccepted + params.units) / offer.units − offer.paymentAccepted`, where `params.units` is this fill's size and `offer.units` the offer's total (integer division rounds down; a negative result counts as zero). Rounding error therefore does not grow with the number of fills, and the fill that exhausts the offer pays the remainder. A priced fill that rounds to zero reverts `ZeroConsiderationFill`. `finalizeSecondaryTradeAgreement` settles the trade through `IssuanceManager.secondaryTransfer`: the LET never moves wallets, and legal ownership transfers through its metadata. Voiding an `ACCEPTED` settlement takes both parties' void requests, or expiry.
* **Exemption pathways.** Every trade travels under an `ExemptionPathway` (`NONE`, `RULE_144`, `SECTION_4A7`, `SECTION_4A1HALF`, `RULE_144A`, `REGULATION_S`). A sell offer may pin one pathway or leave `NONE` so each buyer elects one at acceptance; buy offers must specify one. Only pathways the SPV has enabled can be pinned or elected.
* **Hosting modes.** `HostingMode.DIRECT` delivers the LET to the buyer. `ADMINISTERED` delivers it to an admin multisig and registers the buyer as legal owner. `ADMINISTERED` with a zero `adminMultisig` reverts `MissingAdminMultisig` at `postOffer` (buy offers) and at `acceptOffer`.
* **Void LETs.** A void seller LET keeps its owner and its units, so the ownership and unit checks alone would pass it. Posting a sell offer against it, accepting an offer that settles from it, and finalizing all revert (`CertificateVoided` at post and accept, the LET contract's `VoidCertificate` at finalize).

### Secondary-trade configuration

```solidity
function setMinTradeThreshold(uint256 units, uint256 consideration) external; // onlyAdmin
function setSettlementWindow(uint256 window) external;                        // onlyAdmin
function setDefaultIntegrator(address integrator) external;                   // onlyAdmin, factory-whitelisted
function setSpvThresholdConditions(address[] conditions) external;            // onlyAdmin
function setPathwayThresholdConditions(ExemptionPathway pathway,
    address[] conditions, bool enabled) external;                             // onlyAdmin
function setClosingConditions(address[] conditions) external;                 // onlyAdmin
```

Conditions are layered. Exemption-specific *pathway* conditions and fund-specific *SPV threshold* conditions are read live at post and accept and re-checked at finalization; *closing* conditions are evaluated at finalization. Each layer is set as a whole list. Views: `getSpvThresholdConditions`, `getPathwayThresholdConditions`, `isPathwayEnabled`, `getClosingConditions`, `getMinTradeThreshold`, `getDefaultIntegrator`, `getSettlementWindow`, `getOffer(offerId)`, `getSecondaryEscrow(agreementId)`. See [Conditions](/building-on-the-protocol/reference/conditions).

## Configuration and fees

`setDealRegistry`, `setCorp`, `setIssuanceManager` (all `onlyOwner`); `issuanceManager()`, `getCounterPartyValues(agreementId)`; `computeFee(size)` and `getPlatformPayable()` for the platform fee recipient.

The DealManagerFactory prices primary deals and secondary trades separately, both in basis points of the ticket (`BASIS_POINTS = 10000`):

* **Primary** (`computeFee`, used by `finalizeDeal`): the factory's `getDefaultFeeRatio()`. Despite its name, it returns the per-DealManager override when one is enabled (`setInstanceFeeOverride`) and the platform default otherwise; deployed DealManagers of every version call it by that name.
* **Secondary** (`finalizeSecondaryTradeAgreement`): the factory's `getSecondaryFeeRatio()`, with its own per-DealManager override (`setSecondaryInstanceFeeOverride`) and default (`setDefaultSecondaryFeeRatio`). The integrator's share comes out of that fee.

The factory owner (MetaLeX) sets the defaults and overrides. An override with `enabled = true` and a zero ratio is a real 0% rate. Read the platform defaults, ignoring overrides, with `getUnderlyingDefaultFeeRatio()` / `getUnderlyingDefaultSecondaryFeeRatio()`, and one instance's override with `getInstanceFeeOverride(dm)` / `getSecondaryInstanceFeeOverride(dm)`. A DealManager implementation that does not call `getSecondaryFeeRatio()`, which includes every v4 DealManager, charges `getDefaultFeeRatio()` on secondary trades as well until it is upgraded.

## Events

Declared on DealManager itself: `MinTradeThresholdSet`, `SettlementWindowSet`, `DealRegistrySet`, `CorpSet`, `IssuanceManagerSet` and `DefaultIntegratorSet` (each setter event carries the new value, the old value and the caller). From the libraries, through the interfaces: `DealProposed`, `DealFinalized` (`IDealManagerStorage`); `DealPaidAt`, `DealVoidedAt`, `DealFinalizedAt`, `FeeDistributed` (`LexScrowStorage`); `OfferPosted`, `OfferCancelled`, `OfferAccepted`, `SecondaryTradeAgreementFinalized`, `SecondaryTradeAgreementVoided`, `SecondaryFeeDistributed`, `SpvThresholdConditionsSet`, `PathwayThresholdConditionsSet`, `ClosingConditionsSet` (`ISecondaryTradeStorage`).

## Upgrades

`_authorizeUpgrade` is `onlyOwner` and accepts only the DealManagerFactory's current reference implementation (`NotRefImplementation` otherwise).


# RoundManager

Multi-investor fundraising rounds with escrowed officer signatures

Runs a cyberCORP's multi-investor primary fundraising rounds. A round is identified by a `bytes32 roundId`, and each Expression of Interest (EOI) becomes an agreement identified by a `bytes32 agreementId`.

* **Source:** [`src/RoundManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/RoundManager.sol) / interface [`IRoundManager.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/IRoundManager.sol)
* **Pattern:** UUPS proxy. Round state lives in the `RoundManagerStorage` library and escrow state in the shared `LexScrowStorage` library (see [LeXscroWLite](/building-on-the-protocol/reference/contracts/lexscrowlite)).
* **`DEPLOY_VERSION`:** `"5"`; the RoundManager of a company that has not upgraded reports `"4"` or lower.

## Functions

```solidity
function createRound(Round roundDraft, CyberCertData[] certData)
    external returns (bytes32 roundId);                          // onlyOwner

function submitEOI(bytes32 roundId, EOI eoi, string[] globalValues,
    string[] partyValues, bytes signature, uint256 salt,
    address[] conditions, bytes32 secretHash)
    external returns (bytes32 agreementId, uint256 tokenId);

function allocate(bytes32 agreementId, uint256 allocatedAmount)
    external returns (uint256 tokenId);                          // onlyOwnerOrSelf
function reject(bytes32 agreementId) external;                   // onlyOwner
function reject(bytes32 agreementId, bool isVoidAgreement) external; // onlyOwner
function recallEOI(bytes32 agreementId) external;
function recallEOI(bytes32 agreementId, bool isVoidAgreement) external;

function setRoundEndTime(bytes32 roundId, uint256 newEndTime) external;      // onlyOwner
function closeRoundNow(bytes32 roundId) external;                            // onlyOwner
function setRoundPricePerShare(bytes32 roundId, uint256 price, uint8 priceDecimals) external; // onlyOwner
function setPrimarySecurity(bytes32 roundId, SecurityClass cls, SecuritySeries series) external; // onlyOwner
```

## Views

`getRound(roundId)`, `roundExists`, `getRoundPriceInfo`, `getPrimarySecurity`, `computeFee(size)`, `getPlatformPayable`, `getLexChex` / `setLexChex`, `issuanceManager`, `getEscrowDetails(agreementId)`, `conditionCheck(agreementId)`, `DEPLOY_VERSION`.

## How rounds work

* `createRound` takes a `Round` draft, built with the `RoundLib` helper (series, round type FCFS or FounderApproved, public or private, ticket sizing, raise cap, price per unit, valuation, start and end time, payment token, agreement template, conditions, `allowTimedOffers`, `restrictEndTimeReduction`), plus per-series `CyberCertData` (declared in `src/CyberCorpConstants.sol`). The v5 struct carries the extension's `seriesData` payload, so v4 and v5 `createRound` selectors differ. The `roundId` is derived from the round's economic terms plus the corp address, and a duplicate round reverts `RoundAlreadyExists`.
* The draft must carry an **escrowed officer signature**: `createRound` verifies `roundDraft.escrowedSignature` as the `authorityOfficer`'s EIP-712 signature over the round's terms (`InvalidEscrowedSignature` otherwise). The round reuses that signature to countersign each EOI agreement on the officer's behalf (`CyberAgreementRegistry.signContractWithEscrow`) and as the issuer signature and endorsement on each Ledger Entry Token (LET) it mints. It is checked under the RoundManager's own EIP-712 domain, independent of the registry's `SignatureData` type.
* Investors call `submitEOI` with a minimum and maximum amount inside the round's ticket bounds, and their payment is escrowed immediately. The investor's `signature` is the registry's `SignatureData` signature over the EOI agreement (the RoundManager is its finalizer); where the registry's type has a `signer` field, it names the investor. In an **FCFS** round the EOI is allocated automatically in the same transaction. In a **FounderApproved** round the issuer `allocate`s accepted EOIs or `reject`s them.
* If `allowTimedOffers` is true, each EOI carries its own expiry. Otherwise EOI expiries are ignored and the round's end time bounds every offer.
* `allocate` clamps the allocation to the escrowed amount and the remaining raise cap, enforces the effective minimum ticket, checks the agreement's conditions, finalizes the agreement in the registry, mints the LETs through the IssuanceManager, refunds rounding dust, and releases the escrow less the platform fee.
* `reject` refunds the investor and voids the agreement; `recallEOI` lets the investor reclaim an expired, unallocated EOI. Both take an optional `isVoidAgreement=false` for an agreement already voided directly in the registry.
* `closeRoundNow` and `setRoundEndTime` close or extend a round. A round created with `restrictEndTimeReduction` blocks any end-time reduction (`EndTimeReductionRestricted`).
* `computeFee` / `getPlatformPayable` cover the platform fee on a round. The fee ratio and payable are set on the RoundManagerFactory, which can give one RoundManager its own rate through a per-instance `FeeOverride`.
* `initialize` wires a default LeXcheX credential configuration (credential contract, condition and minter addresses), and `setLexChex` / `getLexChex` manage the credential contract that rounds use. An EOI carries `lexchexDetails` so that, at allocation, an investor without a valid LeXcheX is credentialed automatically through the LeXcheXMinter when the invested amount qualifies (≥ $200k for a natural person, ≥ $1M for an entity, paid in a factory-whitelisted token).

## Events

`RoundCreated`, `RoundEndTimeUpdated`, `RoundClosed`, `EOISubmitted`, `AllocationMade`, `EOIRejected` (declared in `IRoundManager`); `EOIRecalled` and `LexChexUpdated(lexChex, oldLexChex)` (emitted by `setLexChex`) in the contract.

`RoundSnapshotSet`, `RoundingPolicySet` and `PMVCSubseriesLabelSet` are declared in the ABI but never emitted: RoundManager has no snapshot or rounding-policy setter.

## Upgrades

`_authorizeUpgrade` is `onlyOwner` and accepts only the RoundManagerFactory's current reference implementation (`NotRefImplementation` otherwise).


# LeXscroWLite

LeXscroWLite is the atomic deal-closing escrow. It is implemented as the **`LexScrowStorage` library**, linked into the deal and round managers; the repository has no standalone `LeXscroWLite.sol` contract.

* [`src/storage/LexScrowStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/storage/LexScrowStorage.sol) is the escrow library: `Escrow` and `Token` structs, `EscrowStatus` (`PENDING → PAID → FINALIZED`, or `VOIDED`), per-escrow `ICondition` lists, payment intake (`handleCounterPartyPayment`, with exact-amount checks against fee-on-transfer tokens), `finalizeEscrow` (asset delivery plus platform-fee distribution), `voidAndRefund`, and `conditionCheck`. The library declares the escrow events (`DealPaidAt`, `DealVoidedAt`, `DealFinalizedAt`, `FeeDistributed`). Linked library functions run in the manager's context by `DELEGATECALL`, so those logs come from the manager's address.
* [`src/interfaces/ILexScrowStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILexScrowStorage.sol) is the interface both managers implement: the escrow views (`getEscrowDetails`, `conditionCheck`), the fee hooks each manager supplies (`computeFee`, `getPlatformPayable`), and the shared `AgreementConditionsNotMet` error.

Both managers expose the escrow on their own proxies:

* [`DealManager`](/building-on-the-protocol/reference/contracts/dealmanager) escrows payment and Ledger Entry Token (LET) effects across a deal's `proposeDeal` → `signDealAndPay` → `finalizeDeal` lifecycle, with `signToVoid`, `revokeDeal`, `voidExpiredDeal` and `refundVoidedDeal` for stale or voided deals. It runs a parallel `SecondaryEscrow` for secondary-trade settlements. Primary escrows pay the DealManager's primary fee; secondary settlements pay its separate secondary fee.
* [`RoundManager`](/building-on-the-protocol/reference/contracts/roundmanager) does the same across `submitEOI` → `allocate` / `reject` / `recallEOI`.

On each, `getEscrowDetails(agreementId)` returns the `Escrow` struct and `conditionCheck(agreementId)` evaluates the attached conditions. Both managers also implement `onERC721Received` / `onERC1155Received`, so assets can be safe-transferred into escrow.


# CyberAgreementRegistry

An onchain registry of legal-agreement **templates** and executed, multi-party-signed **contracts**. Deals and rounds record their agreements in it.

* **Source:** [`src/CyberAgreementRegistry.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberAgreementRegistry.sol) / interface [`ICyberAgreementRegistry.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ICyberAgreementRegistry.sol)
* **Pattern:** UUPS proxy (`Initializable`, `UUPSUpgradeable`, `BorgAuthACL`); EIP-712 domain `EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)` with name `"CyberAgreementRegistry"`, version `"1"`, the chain id and the registry proxy address. `DOMAIN_SEPARATOR()` and `version()` are public getters.

## Data model

```solidity
struct Template {                              // declared in ICyberAgreementRegistry
    string legalContractUri;   // canonical legal text
    string title;
    string[] globalFields;     // field names common to the whole contract
    string[] partyFields;      // field names filled per signing party
}

struct AgreementData {
    bytes32 templateId;
    string[] globalValues;
    address[] parties;                        // address(0) = open slot
    mapping(address => string[]) partyValues;
    mapping(address => uint256) signedAt;
    uint256 numSignatures;
    address finalizer;
    bool finalized;
    bool voided;
    bytes32 secretHash;
    uint256 expiry;
    address[] voidRequestedBy;
}

// What each party signs (EIP-712)
struct SignatureData {
    bytes32 contractId;
    address signer;            // the party this signature consents for
    string legalContractUri;
    string[] globalFields;
    string[] partyFields;
    string[] globalValues;
    string[] partyValues;      // the signing party's own values
}

bytes32 public constant SIGNATUREDATA_TYPEHASH = keccak256(
    "SignatureData(bytes32 contractId,address signer,string legalContractUri,string[] globalFields,string[] partyFields,string[] globalValues,string[] partyValues)"
); // = 0xe37d17c3ab7740aee31093101f9d27d139a5c3b35324b266efe5d085d6486f05

bytes32 public VOIDSIGNATUREDATA_TYPEHASH; // keccak256("VoidSignatureData(bytes32 contractId,address party)")
```

The `contractId` is `keccak256(abi.encode(templateId, salt, globalValues, parties, secretHash, finalizer))`. `secretHash` and `finalizer` are bound into the id so a front-runner cannot seize the same id with hostile terms. `expiry` is left out so presigned offchain signatures stay verifiable.

The `signer` field binds a signature to one party. A delegate can hold delegations from several parties, and without the field one delegate signature over an agreement would verify for each of them. The string fields are hashed as EIP-712 requires: `legalContractUri` as `keccak256(bytes(uri))`, and each `string[]` as the `keccak256` of the concatenated `keccak256` of its elements.

{% hint style="warning" %}
**Two registry implementations run in production.** The registry proxy `0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134` on Ethereum, Base and Arbitrum runs this implementation: `SIGNATUREDATA_TYPEHASH()` returns `0xe37d17c3…6f05`, and `createContract` derives the six-field id above. The separate zkSync Era registry (`0x07E0a0BeC742f90f7879830bC917E783dA6a6357`) returns `0x49ba7af1fd9b42077b5e2bf090b7deb0f443e9ae99b46ce66b4859e28d670da4`, the type without `signer`, and derives ids from four fields: `keccak256(abi.encode(templateId, salt, globalValues, parties))`. Both report `version()` `"1"`.

Read `SIGNATUREDATA_TYPEHASH()` on the registry you are about to sign against before choosing the typed data or the id formula. Where the flow allows, take the id from the `ContractCreated` event or an `eth_call` of `createContract`. An agreement keeps the id it was created with, so read an existing agreement's id instead of recomputing it.
{% endhint %}

## Functions

```solidity
function createTemplate(bytes32 templateId, string title, string legalContractUri,
    string[] globalFields, string[] partyFields) external;      // permissionless

function createContract(bytes32 templateId, uint256 salt, string[] globalValues,
    address[] parties, string[][] partyValues, bytes32 secretHash,
    address finalizer, uint256 expiry) external returns (bytes32 contractId);

function createStandaloneContractAndSign(string title, string legalContractUri,
    string[] globalFields, string[] partyFields, uint256 salt,
    string[] globalValues, address[] parties, string[][] partyValues,
    uint256 expiry, bytes signature) external returns (bytes32 contractId);
function createStandaloneContractAndSignFor(/* ...as above, plus */ address signer,
    bytes signature) external returns (bytes32 contractId);

function signContract(bytes32 contractId, string[] partyValues, bytes signature,
    bool fillUnallocated, string secret) external;
function signContractFor(address signer, bytes32 contractId, string[] partyValues,
    bytes signature, bool fillUnallocated, string secret) external;
function signContractWithEscrow(address escrowSigner, bytes32 contractId,
    string[] partyValues, bytes signature, bool fillUnallocated, string secret)
    external; // onlyDefinedFinalizer

function setDelegation(address delegate, uint256 expiry) external;
function revokeDelegation() external;

function voidContractFor(bytes32 contractId, address party, bytes signature) external;
function finalizeContract(bytes32 contractId) external; // onlyFinalizerIfSet
```

## Views

`getParties`, `hasSigned`, `getSignatureTimestamp`, `allPartiesSigned`, `getContractDetails`, `getTemplateDetails`, `getSignerValues`, `isVoided`, `isFinalized`, `getAgreementsForParty`, `getVoidRequestedBy`, `getContractJson`, `getDelegation`, `isValidDelegation`, `isValidDelegate`.

## How signing works

* `createTemplate` registers a reusable template (id, title, legal URI, field schema). Anyone can register a template; a duplicate id reverts `TemplateAlreadyExists`.
* `createContract` instantiates an agreement from a template with its global values, parties and per-party values, bounded by `finalizer` and `expiry`. Parties left as `address(0)` are open slots that a later signer can claim with `fillUnallocated`, gated by `secretHash` if one is set.
* `createStandaloneContractAndSign(For)` derives the template (registering it on first use), creates the agreement and signs it in one transaction, so a single-party agreement takes one transaction. Standalone contracts always have `finalizer = address(0)`.
* Each party signs an EIP-712 `SignatureData` payload (contract id, the party's own address as `signer`, legal URI, field schema, global values, and their party values) and submits it through `signContract` (self), `signContractFor` (relayed) or `signContractWithEscrow`. `signContractWithEscrow` takes a pre-escrowed signature that only the agreement's defined finalizer can submit, such as a RoundManager holding an officer's escrowed signature. `signContract` and `signContractFor` verify the signature onchain, and when a finalizer is set, only the finalizer or the signer can submit. `signContractWithEscrow` does not re-verify: it requires a defined finalizer and relies on that vetted finalizer contract for access control.
* A party can give another address standing authority to sign for it (`setDelegation` / `revokeDelegation`, with optional expiry). Signature verification then accepts a valid, unexpired delegate's EIP-712 signature in place of the party's own. Delegation affects signature recovery only, and the registry does not treat the delegate as the party. The delegate signs with `signer` set to the delegating party, so the signature counts for that party alone. `setDelegation` reverts `DelegateZeroAddress`, `DelegateIsSelf` or `ExpiryNotInFuture` (a nonzero expiry at or before the current block); `expiry == 0` means the delegation does not expire. The same delegate check applies to `VoidSignatureData` signatures.
* When all parties have signed, the registry emits `ContractFullySigned`. With no finalizer, the agreement finalizes automatically at that point; otherwise the finalizer calls `finalizeContract`.
* `voidContractFor` records a party's void request (event `VoidRequested`). The agreement is voided when every **allocated** party has requested, when its nonzero expiry has passed, or when the proposing party (index 0) voids while still the only signer. Unfilled `address(0)` slots in an open agreement cannot request and do not count toward unanimity, but a party who fills a slot must then also request. The finalizer can submit void requests without a signature; anyone else needs the party's EIP-712 `VoidSignatureData` signature. A finalized agreement cannot be voided.

{% hint style="info" %}
`expiry == 0` means **no deadline** in the void path, as in signing and finalization. The expired branch never applies, so a zero-expiry agreement voids only by unanimous request of its allocated parties, or by the proposer while sole signer.
{% endhint %}

`getContractJson(contractId)` returns the agreement as a JSON string (template id, title, legal URI, global fields, per-party values with `signedAt`, signature count, `isComplete`, `voided`, `voidRequestedBy`, `finalized`). Titles, URIs, field names and values are JSON-escaped, so a quote or backslash typed into a field cannot break the document.

## Events

`TemplateCreated`, `ContractCreated`, `AgreementSigned` and `ContractFullySigned` are declared in `ICyberAgreementRegistry`; `ContractFinalized`, `VoidRequested`, `ContractVoided`, `DelegationSet` and `DelegationRevoked` in the contract. The registry proxy emits all of them.


# SafeCertificateConverter

Computes a plan for converting a SAFE Ledger Entry Token (LET) into equity, from a RoundManager's round data.

* **Source:** [`src/converters/SafeCertificateConverter.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/converters/SafeCertificateConverter.sol)
* **Implements:** `ICertificateConverter`

> **Stub.** The body of `computeConversion` is commented out, and the function returns an empty `ConversionPlan`. The intended algorithm is kept in the source as a comment. Do not use this contract for live conversions.

## Interface

```solidity
function computeConversion(
    address roundManager,
    bytes32 roundId,
    address certPrinter,
    uint256 tokenId
) external view returns (ConversionPlan memory plan);
```

## Intended algorithm (from the source comments)

1. Read the source SAFE LET's `investmentAmountUSD` and `issuerUSDValuationAtTimeOfInvestment`.
2. Read round data from the RoundManager: the cap table snapshot's `cCapUsed`, the rounding policy, the round price, and the primary security class and series.
3. Compute the SAFE price as `PMVC / CCap`, and take the lower of the SAFE price and the round price as the price basis.
4. Compute `shares = investmentAmount / priceBasis`, applying the round's rounding policy (floor, ceil or round-half-up).
5. Return a `ConversionPlan` with the share count, price basis, and target class and series.


# LexChex / LeXcheXBadge / LeXcheXMinter

MetaLeX's onchain credentials. Every credential is a **soulbound** (non-transferable) NFT implementing [ERC-5484](https://eips.ethereum.org/EIPS/eip-5484). Two credential contracts are deployed side by side:

* **LeXcheX** is the accreditation credential, with one `Accreditation` record per token.
* **LeXcheXBadge** is the unified credential registry (`VERSION = 2`). One deployment carries KYC/AML facts, accreditation statuses and SPV-scoped entitlements as typed fact-keys. Its admins (its BorgAuth) run it and can delegate issuing authority per fact-key to other issuers, so several credentialing operators can share one registry.
* **Sources:** [`src/creds/lexchex.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/creds/lexchex.sol), [`src/creds/lexchexBadge.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/creds/lexchexBadge.sol), [`src/creds/lexchexMinter.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/creds/lexchexMinter.sol)
* **Interfaces:** [`ILexChex.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILexChex.sol), [`ILexChexBadge.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ILexChexBadge.sol)

## ERC-5484 (soulbound)

Both interfaces extend `IERC5484`, which defines `burnAuth(tokenId)` returning a `BurnAuth` enum (`IssuerOnly`, `OwnerOnly`, `Both`, `Neither`) and an `Issued` event. Credentials cannot move between wallets. LeXcheXBadge tokens are **never burnable** (`burnAuth` always returns `Neither`): revocation is by voiding, so every credential, whether voided, expired or superseded, stays onchain for audit.

## LeXcheX (accreditation credential)

```solidity
function mint(address to, Accreditation acc) external returns (uint256);
function burn(uint256 tokenId) external;
function void(uint256 id, string reason) external;               // onlyOwner
function setAccreditation(uint256 tokenId, Accreditation acc) external;
function accreditations(uint256 tokenId) external view returns (Accreditation);
function getAccreditation(uint256 tokenId) external view returns (Accreditation);
function getAccreditationByOwner(address owner) external view returns (uint256);
function getTokenIdsByOwner(address owner) external view returns (uint256[]);
function hasValidLexCheX(address owner) external view returns (bool);
function isValid(uint256 tokenId) external view returns (bool);
function balanceOf(address owner) external view returns (uint256);
function burnAuth(uint256 tokenId) external view returns (BurnAuth);
```

The `Accreditation` struct (name, type, jurisdiction, contact, issuance and expiry dates, void reason, backing agreement id and registry, authority signature) is defined in [`src/creds/storage/lexchexStorage.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/creds/storage/lexchexStorage.sol). `hasValidLexCheX(owner)` answers whether an address holds a currently valid credential, and `isValid` applies the three-part test: issued, not voided, not expired.

## LeXcheXBadge (unified credential registry)

Each token is an immutable `Credential`. Its `asserts` bitmask of `K_*` fact-keys is the sole authority axis: a field answers a read only when its key is asserted.

* Value keys: `K_INVESTOR_TYPE`, `K_INVESTOR_JURISDICTION`, `K_LOOKTHROUGH_JURISDICTION` (the ICA §3(c)(1)(A) classification, kept separate from physical jurisdiction), `K_US_STATE`, `K_BO_COUNT`, `K_DATA`.
* Status keys: `K_ACCREDITED`, `K_QP`, `K_QIB`, `K_BAD_ACTOR_CLEAR`, `K_NON_US`.
* SPV-scoped keys: `K_SPV_WHITELIST`, `K_SYNDICATE`.

The `Credential` struct also carries `investorName`; the `issuer` address that minted it, which consumers filter on to choose whose word they take; a `scope`, naming the SPV the credential is about (any key may carry one, and the SPV-scoped keys require it); issuance and expiry dates; the backing `agreementId`; and an `evidenceHash` anchoring the offchain diligence record.

```solidity
// Issuing authority
function setIssuerKeys(address issuer, uint256 keys) external;                 // onlyAdmin
function issuerKeys(address issuer) external view returns (uint256);

// Lifecycle
function mint(address to, Credential cred) external returns (uint256 tokenId); // admin, or issuer within its granted keys
function supersede(uint256 staleTokenId, Credential cred, string reason)
    external returns (uint256 tokenId);                                        // void + mint, under the same rules
function void(uint256 tokenId, string reason) external;                        // credential's own issuer, or any admin

function sweep(address holder) external;                       // permissionless
function sweepHolders(address[] holders) external;             // permissionless
function sweepTokens(uint256[] tokenIds) external returns (uint256 evicted); // permissionless

function isValid(uint256 tokenId) external view returns (bool);

// Filtered reads: full form plus shortcuts (kindKey only / + issuers / + scope)
function hasValidCredentialOf(address owner, uint256 kindKey,
    address[] issuers, address scope, address hook, bytes hookData) external view returns (bool);
function hasValidCredentialOf(address owner, uint256 kindKey) external view returns (bool);
function hasValidCredentialOf(address owner, uint256 kindKey, address[] issuers) external view returns (bool);
function hasValidCredentialOf(address owner, uint256 kindKey, address scope) external view returns (bool);
function getMostRecentValidWith(address owner, uint256 kindKey,
    address[] issuers, address scope, address hook, bytes hookData)
    external view returns (uint256 tokenId, bool found);        // + the same three shortcuts
function earliestValidIssuance(address owner, uint256 kindKey,
    address[] issuers, address scope, address hook, bytes hookData)
    external view returns (uint64);                             // + a kindKey-only shortcut

function hasValidWhitelistFor(address owner, address spv) external view returns (bool);
function hasValidSyndicateFor(address owner, address spv) external view returns (bool);
function hasValidLexCheX(address owner) external view returns (bool); // LeXcheX-compatible read

function getInvestorType(address owner) external view returns (InvestorType value, uint64 expiry);
function getUsState(address owner) external view returns (bytes2 value, uint64 expiry);
function getEffectiveBeneficialOwnerCount(address owner) external view returns (uint32 value, uint64 expiry);
function getInvestorJurisdiction(address owner) external view returns (string value, uint64 expiry);
function getLookThroughJurisdiction(address owner) external view returns (string value, uint64 expiry);
function getData(address owner) external view returns (bytes value, uint64 expiry);

function getTokenIdsByOwner(address owner) external view returns (uint256[]); // full audit history
function getActiveTokenIds(address owner) external view returns (uint256[]);  // not voided, not yet swept; may include expired, so check isValid per id
function getCredential(uint256 tokenId) external view returns (Credential);
function getCredentialByOwner(address owner) external view returns (uint256);
```

Key semantics:

* **Per-issuer authority.** `setIssuerKeys(issuer, keys)` (admin-only) is the entire grant. `mint` rejects any asserted key outside the caller's mask (`LexChexBadge_KeysNotAuthorized`), and a delegated issuer neither needs nor receives a BorgAuth role. Admins (checked through `AUTH`, so role adapters count) may assert any key. `mint` stamps `cred.issuer` with the caller. `void` is issuer-scoped: an issuer voids only its own credentials, with no grant required, so an issuer cut off from minting can still clean up its own work, and an admin can void any credential. `supersede` voids and re-issues in one call under the same rules.
* **Immutable, append-only.** Credentials are never edited or burned. To change a fact, mint a newer credential (the most recent valid one wins); to retract one, void it.
* **Union reads.** `hasValidCredentialOf(owner, kindKey, …)` passes when the owner's valid credentials *together* assert every fact-key in `kindKey`. The keys need not sit on one credential.
* **Query filters.** Every read (`hasValidCredentialOf`, `getMostRecentValidWith`, `earliestValidIssuance`) takes the same four optional filters, each off by default: `issuers` (only credentials from these issuers count; empty accepts any), `scope` (the SPV a credential must name; zero accepts any), and `hook` / `hookData` (an `ICredentialQueryHook` the caller supplies to test each credential, typically to interpret `Credential.data`, whose schema the badge never learns). Filters apply per credential. A fully empty query reverts (`LexChexBadge_EmptyQuery`), so a blank parameterization cannot admit everyone. Shortcut overloads cover the common forms.
* **Authoritative reads.** `getMostRecentValidWith` resolves the owner's authoritative credential: the most recent valid one carrying *all* of `kindKey` on a single record (ties go to the higher tokenId) that clears every filter. The value getters run this read. Use it directly when matches can contradict each other, for example two credentials naming different U.S. states, or an issuer tier where a newer seat demotes an older one.
* **Value getters return `(value, expiry)`**, where `expiry` belongs to the credential answering the read. When no valid credential asserts the fact, they return the field's empty value (`0`, `""`, `bytes2(0)`) instead of reverting. The badge does not interpret an empty answer: each downstream condition decides whether an unknown fails open or closed.
* **Bounded active set.** Compliance reads scan the holder's active set: entries not voided and not yet swept. `void` evicts at once, but expiry eviction waits. An expired credential stays in the set (and in `getActiveTokenIds`) until a permissionless `sweep*` keeper call evicts it, with `sweepTokens` working in calldata-bounded batches. Validity-sensitive reads check expiry per credential, but clients consuming `getActiveTokenIds` must apply `isValid` per id. The full ERC-721 enumeration is kept for audit.

**Events:** `CredentialIssued`, `CredentialVoided`, `CredentialSwept`, `IssuerKeysUpdated` (the issuer's complete new key set, not a delta), plus ERC-5484 `Issued`.

## LeXcheXMinter

The issuance gateway for LeXcheX credentials (`initialize(_auth, _lexchex, _dealRegistry, _treasury)`):

* `requestMint` verifies an EIP-712 **authority signature** from a BorgAuth admin over the `MintRequest`, takes the mint fee to the treasury, creates and signs the backing agreement in the CyberAgreementRegistry, mints the LeXcheX, and finalizes the agreement. The subject's agreement signature is relayed to the registry's `signContractFor`, so it must use the configured registry's `SignatureData` type, with the subject as `signer` where the type has that field (see [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry#data-model)).
* `requestMintFor` is an admin-only variant that skips the authority-signature check. RoundManager auto-credentialing uses it during allocation.
* `adminMintFor` is an admin-only mint without a backing agreement.
* `requestRenewal` and `requestRenewalFor` are the renewal counterparts. The signed subject is bound to the actual token owner, which prevents cross-account renewals.
* Configuration: `setLexchex`, `setDealRegistry`, `setTreasury` (all `onlyOwner`).

**Events:** `MintRequested`, `MintCompleted`, `RenewalRequested`, `RenewalCompleted`.

## Where the protocol reads credentials

* A LexChex condition (see [Conditions](/building-on-the-protocol/reference/conditions)) wraps the validity checks so they can gate issuance, rounds, scripification, deals and secondary trades. The secondary-trading conditions read the badge's fact-keys (accreditation, QP/QIB, Reg S non-US status, jurisdictions, beneficial-owner counts).
* The [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) look-through holder tally reads beneficial-owner counts and US residency from a configured LeXcheXBadge.
* The LeXcheX app and oracle in [metalex-webapp](https://github.com/MetaLex-Tech/metalex-webapp) run the offchain verification behind a mint.


# CertificateUriBuilder

Builds the fully onchain token-URI metadata (JSON and SVG) of every Ledger Entry Token (LET).

* **Sources:** [`src/CertificateUriBuilder.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CertificateUriBuilder.sol), [`src/CertificateImageBuilderContract.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CertificateImageBuilderContract.sol), [`src/CertificateImageBuilder.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CertificateImageBuilder.sol), [`src/CertificateImageContentBuilder.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CertificateImageContentBuilder.sol), [`src/libs/CertificateText.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/libs/CertificateText.sol)
* **Interfaces:** [`IUriBuilder.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/IUriBuilder.sol), [`ICertificateImageBuilder.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ICertificateImageBuilder.sol)
* **Pattern:** UUPS proxy (`UUPSUpgradeable`, `BorgAuthACL`) with `initialize(address _auth)`. SVG generation sits in a separately deployed image-builder contract, set with `setImageBuilder(address)` (`onlyOwner`, emits `ImageBuilderUpdated`), to stay under contract-size limits. JSON string helpers shared with other contracts live in the [`JsonLib`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/libs/JsonLib.sol) library.

## What it produces

`LedgerEntryToken.tokenURI(tokenId)` delegates to the URI builder configured on the IssuanceManager, which assembles a `data:application/json;base64,…` metadata document containing:

* The standard title and type fields and `attributes` (current owner and name, amounts, corp identity, security type and series, certificate URI).
* An `image`: an onchain-rendered SVG (see [The certificate image](#the-certificate-image)).
* The corp identity fields and the LET's details. `investmentAmountUSD`, `issuerUSDValuationAtTimeOfInvestment` and `unitsRepresented` are 18-decimal quantities that `from18DecimalsToString` renders with **two decimal places, truncated** (it divides by `1e16`, so `1.234e18` becomes `"1.23"`). The live `unitsReserved`, read from the LET contract, is formatted the same way and left out when the LET contract has no such getter. These strings are display values: read exact amounts with the LET contract's `getCertificateDetails(tokenId)`, not from the metadata.
* Extension-provided JSON, in order: the corp-level fragment (`CyberCorp.getExtensionURI`, skipped when the issuer has none), then the LET's own section from the LET contract's [extension](/building-on-the-protocol/reference/extensions). A V3 extension that answers `supportsResolvedExtensionData()` with `true` renders the whole certificate through `getResolvedExtensionURI(printer, tokenId)`, merging the LET, series and (for shares) class scopes. A V1 or V2 extension renders the LET payload alone through `getExtensionURI(extensionData)`. The extension call is not caught, so a reverting extension reverts the whole `tokenURI` call.
* The `endorsementHistory` array (endorser, timestamp, registry, agreement id, investor name and address). The first entry also carries `purchaseAgreementDetails` read from the agreement registry: the global fields, `companyDetails` and `investorDetails` from the two party slots, and the `digitalSignature`. That read is guarded, so a registry that cannot answer leaves the endorsement in place without the enrichment.
* `currentOwner`.
* The `restrictiveLegends` array of structured `RestrictiveLegend` records. Plain-string legends are converted through `legacyLegendsToRestrictiveLegends`.

Every string taken from company or holder input is JSON-escaped (`JsonLib.jsonEscape`), in the builder and in each extension's fragment. The metadata is built onchain at read time, so a LET renders from any node or explorer with no external service.

## The certificate image

The image builder draws a "Ledger Entry Token" certificate from a `CertificateSVGParamsV2` (the `CertificateSVGParams` fields plus issuer and owner addresses, consideration, transfer restrictions and void status):

* the company name, token id and a status pill (**active** or **voided**, with a VOIDED stamp across a void certificate);
* **Issuer** (company name and the cyberCORP address) and **Registered Owner** (holder name and address);
* class and series;
* **Units** and **Consideration**: for shares and units, the units held and the price per share or unit; for convertibles (SAFE, SAFT, SAFTE, token purchase agreement, token warrant, convertible note), `1` and the whole amount;
* **Issue Date**, from the LET contract's `issueTimestamp` (blank when unknown);
* the authorizing officer's name and title;
* up to six transfer restrictions, taken from the restrictive legends (text, or title when the text is empty).

Names and titles are XML-escaped and truncated to fit the artwork (`CertificateText.xml`); the full values stay in the JSON. The builder calls `buildCertificateSVGV2`; if that returns nothing and the issue date is known, it calls `buildCertificateSVG` instead, so the URI builder and the image builder can be upgraded in either order. Reads of the issuer, issue date and void status tolerate LET contracts and managers that lack those functions. If no SVG comes back, the `image` field is left empty and the rest of the URI still builds.

## Interface

`IUriBuilder` exposes `buildCertificateUri` and `buildCertificateUriNotEncoded` (raw JSON, no base64 wrapper), each with two overloads: one taking a `string[] certLegend`, one taking `RestrictiveLegend[]`. Public helpers include `securityClassToString`, `securityClassToUnit`, `securitySeriesToString`, `restrictiveLegendsToJson`, `from18DecimalsToString` and `stripIpfsPrefix`.

The URI builder is set on the IssuanceManager (`uriBuilder()` / `setUriBuilder`). This page summarizes the builder; the source has the full function set.


# Factories

Factories deploy cyberCORPs and their contracts.

## CyberCorpFactory

The top-level entry point.

* **Source:** [`src/CyberCorpFactory.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberCorpFactory.sol)
* **Inherits:** `UUPSUpgradeable`, `BorgAuthACL`

```solidity
function deployCyberCorp(bytes32 salt, string companyName, string companyType,
    string companyJurisdiction, string companyContactDetails,
    string defaultDisputeResolution, address _companyPayable,
    CompanyOfficer _officer)
    external returns (address cyberCorp, address auth, address issuanceManager,
                      address dealManager, address roundManager);

function deployCyberCorpAndCreateOffer(/* ... */) external returns (/* corp suite + deal */);
function deployCyberCorpAndCreateRound(/* ..., bytes escrowedSignature,
    bytes metadataSignature, ... */) external returns (/* corp suite + roundId */);
function computeDeploymentSalt(bytes32 salt, /* company fields */,
    address _companyPayable, CompanyOfficer _officer) public pure returns (bytes32);
```

`deployCyberCorp`:

1. Checks that every sub-factory namespaces CREATE2 salts by caller (see [Sub-factories](#sub-factories); `IncompatibleComponentFactory` otherwise), then replaces `salt` with `computeDeploymentSalt(...)`, a hash of the salt, the company details, the payout address and the officer. The predicted addresses therefore commit to that configuration. Anyone may submit the call (so it works inside a multicall), but only with the same officer and settings, and a direct deployment through a sub-factory lands in the caller's own namespace, so nobody can squat a company's predicted addresses.
2. Deploys a `BorgAuth` ACL with `CREATE2` and grants the officer level `200`.
3. Deploys the `IssuanceManager`, `CyberCorp`, `DealManager` and `RoundManager` through their sub-factories and initializes them.
4. Grants the `IssuanceManager`, `DealManager` and `RoundManager` BorgAuth level `99`, and the `CyberCorp` level `200`.
5. Emits `CyberCorpDeployed`.

The new `BorgAuth` is constructed with the factory as its owner (level `99`), and the factory does not renounce that level; see [Access control](/building-on-the-protocol/reference/access-control#roles-the-protocol-assigns).

The `deployCyberCorpAndCreate*` variants also create LET contracts and open a deal or a round in the same transaction. Their `CyberCertData` struct (defined once in `CyberCorpConstants.sol` and shared by the factory, deal and round paths) carries, per LET contract, the name, symbol, URI, security class and series, the certificate [extension](/building-on-the-protocol/reference/extensions) contract, an extension-encoded `seriesData` payload, and the default legend.

`deployCyberCorpAndCreateRound` also requires `metadataSignature`: the officer's EIP-712 signature (domain name `"CyberCorpFactory"`, version `"1"`) over the deployment metadata that the escrowed round signature does not cover. That metadata is the corp salt, payout address, round flags, officer, company details, extension data, round party values, legal details, `CyberCertData` and condition addresses. A missing or wrong signature reverts `InvalidMetadataSignature`, so a caller cannot substitute those fields.

Setters (`onlyOwner`): `setStable`, `setIssuanceManagerFactory`, `setCyberCorpSingleFactory`, `setCyberAgreementFactory`, `setDealManagerFactory`, `setRoundManagerFactory`, `setLexchexAuth`.

### Adding a RoundManager to a company without one

`RoundManagerUpgradeHelper.upgradeCorp(corp, salt)` (in `src/helpers/`) deploys a RoundManager for a cyberCORP that has none and wires it in. The caller must hold `OWNER_ROLE` on the corp's BorgAuth, the helper itself needs that role for the call, and it renounces the role (`zeroOwner`) when done.

## Sub-factories

`CyberCorpFactory` composes:

| Factory                  | Deploys                                                                                                                                         |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `CyberCorpSingleFactory` | the `CyberCorp` proxy; holds the reference implementation that gates `CyberCorp` upgrades.                                                      |
| `IssuanceManagerFactory` | the `IssuanceManager`; holds the `LedgerEntryToken` and `CyberScrip` reference implementations that the IssuanceManager's own beacons point at. |
| `DealManagerFactory`     | the `DealManager`; also holds the platform fee settings.                                                                                        |
| `RoundManagerFactory`    | the `RoundManager`; also holds the platform fee settings and the payment-token whitelist.                                                       |

Each sub-factory's `deploy*(salt)` is permissionless but namespaced: it deploys at `deploymentSalt(salt, msg.sender)` = `keccak256(abi.encode(msg.sender, salt))`, so each caller has its own address space. Predict an address with the two-argument `compute*Address(salt, deployer)`; the one-argument form uses the caller. Each sub-factory publishes the reference implementation for new deployments and upgrades (`getRefImplementation`, plus `getCyberCertPrinterRefImplementation` / `getCyberScripRefImplementation` on the IssuanceManagerFactory); see [Upgrade model](/building-on-the-protocol/reference/upgrade-model).

Every company shares one `CyberAgreementRegistry`, passed in as `registryAddress`.

## Specialised factories

These build on the same primitives for specific structures:

| Factory           | Source                    | Purpose                                                                                                                                                                                       |
| ----------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PumpCorpFactory` | `src/PumpCorpFactory.sol` | Deploys cyberCORPs configured for **ACE** (token-to-equity) offerings. Uses the same salt binding and the same officer metadata signature, under the EIP-712 domain name `"PumpCorpFactory"`. |
| `MetaDAOFactory`  | `src/MetaDAOFactory.sol`  | Deploys MetaDAO futarchy-governed SPC structures.                                                                                                                                             |
| `ParentCoFactory` | `src/ParentCoFactory.sol` | Deploys parent/subsidiary cyberCORP structures.                                                                                                                                               |

The table summarizes them; their source has the exact constructors and deployment parameters.


# Certificate extensions

Certificate and corp extensions: per-security-type data and JSON rendering

Extensions are pluggable metadata contracts. Each LET contract (a `LedgerEntryToken` deployment) points at **one** extension contract, set when the LET contract is created. The extension decodes the instrument-specific attributes that appear in the `tokenURI` of each Ledger Entry Token (LET), from up to three opaque payloads, one per scope:

* **LET scope:** `CertificateDetails.extensionData` on each LET, decoded by the base `ICertificateExtension` surface (`supportsExtensionType`, `getExtensionURI(data)`).
* **Series scope:** the LET contract's `seriesData`, terms shared by every LET it issues.
* **Class scope:** the `classData` of the LET contract's security class on the IssuanceManager (`SecurityClassInfo`). Only `ShareExtensionV3` reads it.

A V1 or V2 extension renders the LET payload alone. A V3 extension (`ICertificateExtensionV3`) renders the whole certificate: it answers `supportsResolvedExtensionData()` with `true`, and `getResolvedExtensionURI(printer, tokenId)` reads every scope through the LET contract, merges them and returns one JSON section. The [CertificateUriBuilder](/building-on-the-protocol/reference/contracts/certificateuribuilder) probes for that function and falls back to the LET payload for V1 and V2 extensions. Each V3 extension also offers typed helpers that the shared interface cannot declare, because their return types differ per instrument: `decodeExtensionData(bytes)`, `encodeExtensionData(<stored shape>)`, and `resolveCert(printer, tokenId)`, which returns the resolved typed data.

{% hint style="warning" %}
A V3 extension calls `getSeriesInfo` on the LET contract, and `ShareExtensionV3` also calls `getPrinterClassId` / `getSecurityClass` on the IssuanceManager. LET contracts and IssuanceManagers below v5 lack some of these, and the URI builder does not catch an extension revert, so binding a V3 extension to a LET contract of a company that has not upgraded to v5 makes `tokenURI` revert. Bind V3 extensions only to LET contracts of v5 companies.
{% endhint %}

Each extension is its own UUPS-upgradeable contract (in [`src/storage/extensions/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/storage/extensions/README.md)). Every version stays deployed: a LET contract's payloads are decoded by the extension version it points at, and the V3 extensions have their own proxies (addresses in [Deployments](/building-on-the-protocol/reference/deployments)). Every extension JSON-escapes the strings it writes.

## ShareExtension / ShareExtensionV3

Preferred and common stock. NVCA-aligned terms, rendered to JSON with `JsonLib`; prices and share quantities are 18-decimal fixed-point. The `ShareCertData` a LET renders combines:

| Part              | Contents                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SeriesTerms`     | Series name, par value, authorized shares, original issue price, liquidation preference (multiple and type), seniority, dividends, conversion (`targetConversionSeriesId` string, conversion price, anti-dilution: broad- or narrow-based weighted average, full ratchet, none), voting (`votesPerShare`, board seats, class and series votes), redemption, pay-to-play, registration rights, pro rata rights, information rights and drag-along, each rights flag paired with a terms URI. |
| `CertificateData` | Per-LET facts, including DGCL §156 partly paid stock (`isPartlyPaid`, `amountPaid`, `totalConsideration`), representation type (certificated, uncertificated or tokenized) and holding-period tacking.                                                                                                                                                                                                                                                                                      |
| Lists             | Mandatory-conversion triggers, special voting rights, transfer restrictions and split history.                                                                                                                                                                                                                                                                                                                                                                                              |

`ShareExtension` (V1) stores a whole `ShareCertData` on each LET. `ShareExtensionV3` stores a `ShareCertDataLayer` at each of the three scopes (class, series, LET) and merges them:

* `certificateData` and `terms` are single values: the most specific layer that sets one wins (LET, then series, then class).
* The four lists append from class to series to LET, so a LET payload cannot drop a class-level restriction. A layer's overwrite flag (`overwriteConversionTriggers`, `overwriteVotingRights`, `overwriteTransferRestrictions`, `overwriteSplitHistory`) makes its list replace the lists above it. To clear a section, set an empty list and its flag.
* `terms`, `conversionTriggers` and `splitHistory` must sit on the same layer, because a stock split rescales all three. The series is the recommended scope.

Payloads carry no version tag, so a LET contract's payloads and its extension must be the same generation. Bind `ShareExtensionV3` when the LET contract is created, and do not point a LET contract that already holds whole `ShareCertData` payloads at it. The merge code lives in the linked `ShareCertDataLayerLib`. The companion `ShareExtensionLogic` contract offers validation and update helpers over a layer (`validateShareData`, `updateSeriesTerms`, `recordStockSplit`, …) for offchain tooling and scripts; a helper that edits an existing section reverts when the layer does not set that section.

## SAFEExtension / SAFEExtensionV3

Simple Agreement for Future Equity. Per-LET `SAFEData` holds a `customProvisions` string; the SAFE economics (investment amount, valuation) live in the base `CertificateDetails`. `SAFEExtensionV3` adds series-scope `SAFESeriesData` (series name, governing-document URIs, custom provisions), rendered as a `SAFESeriesDetails` section next to the LET fields.

Feeds [`SafeCertificateConverter`](/building-on-the-protocol/reference/contracts/safecertificateconverter).

## ACESAFEExtension / ACESAFEExtensionV3

The ACE SAFE variant for token-to-equity conversions, used by [PumpCorpFactory](/building-on-the-protocol/reference/factories#specialised-factories). Per-LET `ACESAFEData` holds the denomination token and custom provisions. `ACESAFEExtensionV3` adds series-scope `ACESAFESeriesData` (series name, denomination token, governing-document URIs, custom provisions).

## SAFTExtension / SAFTExtensionV2 / SAFTExtensionV3

Simple Agreement for Future Tokens.

* Unlock schedule (start-time type, start time, period, interval type)
* Cliff period and cliff percentage
* V2 adds a `customProvisions` string
* V3 adds series-scope `SAFTSeriesData` (series name, token generation event date, governing-document URIs, custom provisions)

## SAFTEExtension / SAFTEExtensionV2 / SAFTEExtensionV3

Simple Agreement for Future Tokens or Equity. SAFT-style unlock fields plus the token-amount calculation method, minimum company reserve, token premium multiplier, and protocol valuation at time of investment. V2 adds `customProvisions`; V3 adds series-scope `SAFTESeriesData` (series name, governing-document URIs, custom provisions).

## TokenWarrantExtension / TokenWarrantExtensionV2 / TokenWarrantExtensionV3

Token warrants.

* Exercise price and method (per token or per warrant)
* Token-amount calculation method (equity pro rata to company reserve or to token supply, dollar pro rata to protocol valuation)
* Unlock parameters (start, period, cliff, interval)
* Latest expiration time
* V2 adds `customProvisions`; V3 adds series-scope `TokenWarrantSeriesData` (series name, underlying-token description, governing-document URIs, custom provisions)

For the SAFE, ACE SAFE, SAFT, SAFTE and token-warrant V3 extensions, and for fund interests, the LET and series scopes hold different fields, so `resolveCert` returns the two side by side (`{series, certificate}`) instead of merging them.

## FundInterestExtensionV3

Fund (LP) interests, with separate LET and series data:

* Per-LET `FundInterestData`: `acquisitionDate`, `tackedFromAcquisitionDate` (the Rule 144 tacking anchor that `HoldingPeriodCondition` reads), an affiliate or control-person flag, and custom provisions.
* Series-scope `FundInterestSeriesData`: interest class, fund entity type, ICA exception relied upon, management fee and carried interest (bps), distribution waterfall position, governing-document URIs, and security identification fields.

The typed `IFundInterestExtension` interface exposes `acquisitionDate`, `tackedFromAcquisitionDate` and `withTackedFrom`, so consumers read and rewrite the payload through the extension instead of decoding the struct.

## CyberCorp extensions

A parallel family implements `ICyberCorpExtension` and attaches to the **corp** instead of a LET contract, through `CyberCorp.setExtension(extension, extensionType)` / `setExtensionData(bytes)` (both `onlyOwner`):

| Extension                                     | Adds                                                                                                                                                                    |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CyberCorpExtension` / `CyberCorpExtensionV2` | Corp-level profile metadata (website, business line, entity id, metadata URI; V2 adds investor-relations URI and transfer agent).                                       |
| `CyberCorpComplianceExtension`                | Compliance parameters (ERISA allowance, ownership bounds, holder count cap, CFIUS-approval flag, holder restrictions, fee details).                                     |
| `CyberCorpFundExtension`                      | Per-SPV fund metadata (entity type, ICA exception relied upon, Reg S issuer category, holder cap, portfolio holdings, provenance attestation, governing-document URIs). |

## Registering an extension

The extension and its series payload are supplied when the LET contract is created:

```solidity
issuanceManager.createCertPrinter(
    ledger, name, ticker, certificateUri,
    securityType, securitySeries,
    address(extension),   // decodes the LET, series (and class) payloads
    seriesData            // "" when the extension has no series section
);
```

Per-LET `CertificateDetails.extensionData` is then `abi.encode`d to the extension's expected struct at issuance (for `ShareExtensionV3`, a `ShareCertDataLayer`; an empty payload takes every section from the series and class). The series payload can be updated later through `LedgerEntryToken.setSeriesData` (IssuanceManager or admin), and a share class payload through `IssuanceManager.updateSecurityClass`.


# Hooks

The protocol uses two kinds of hook: **transfer-restriction hooks**, consulted on token transfers and register changes, and a **Uniswap v4 hook** for LiquiLeX pools.

## Transfer-restriction hooks

### `ITransferRestrictionHook`

```solidity
interface ITransferRestrictionHook {
    // Delivery: may the token move between these addresses?
    function checkTransferRestriction(
        address from,
        address to,
        uint256 tokenId,   // token id for LETs; amount for scrip
        bytes memory data
    ) external view returns (bool allowed, string memory reason);

    // Registration: may the holder of record change from `from` to `to`?
    function checkLegalTransferRestriction(
        address from,      // current holder of record (never address(0))
        address to,        // incoming holder of record
        uint256 tokenId,
        bytes memory data
    ) external view returns (bool allowed, string memory reason);
}
```

The two checks answer different questions. `checkTransferRestriction` governs possession; `checkLegalTransferRestriction` governs the register and is never called at original issuance. `BaseTransferHook` answers the registration check with the delivery answer unless a hook overrides it, so a registration is at least as restricted as a delivery. A hook used by a v5 LET contract must implement both functions: a hook that implements only `checkTransferRestriction` reverts when the LET contract asks it about a registration.

`LedgerEntryToken`, `CyberScrip` and `CyberShares` consult these hooks:

* `CyberScrip` holds an **array** of hooks (`setRestrictionHook` replaces the whole list). Every hook must allow the transfer, or it reverts `RestrictedTransfer(reason)`. The hooks run on transfers **and mints** (any move to a nonzero address), so scripifying to a recipient needs that recipient to pass them. Burns skip them.
* `LedgerEntryToken` holds per-LET hooks (`setRestrictionHook(id, hook)`) and a `globalRestrictionHook` (`setGlobalRestrictionHook`). It calls `checkTransferRestriction` when a token moves between two nonzero addresses and `checkLegalTransferRestriction` when the holder of record changes after issuance, alongside its own stop-transfer flags (set with `setGlobalLegalTransferable` / `setTokenLegalTransferable`). A failing hook reverts `TransferRestricted(reason)`.
* `CyberShares` checks its global hook on `transfer` and `transferFrom`.

### Implementations

In [`src/hooks/transfer/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/hooks/transfer/README.md):

| Hook                    | Purpose                                                                                                                                                                   |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BaseTransferHook`      | Abstract base: an admin `setEnabled` switch (a disabled hook allows everything) and the default registration check.                                                       |
| `WhitelistTransferHook` | Allows a transfer when both sender and recipient are whitelisted, and a mint when the recipient is whitelisted. Admin functions `setWhitelisted` / `batchSetWhitelisted`. |

For per-LET switches, use the LET contract's own `setTokenTransferable` (delivery) and `setTokenLegalTransferable` (registration); see [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken#delivery-and-registration-gates).

## Uniswap v4 hook

### `MetalexIssuerFeeHook`

In [`src/hooks/uniswap/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/hooks/uniswap/README.md). A Uniswap v4 hook for **LiquiLeX** AMM pools that splits swap fees between MetaLeX and the issuer, so a scrip/stablecoin pool pays the issuer onchain on every trade.

* Per-pool configuration through `setPoolConfig` (`onlyAdmin`): a `PoolFeeConfig` with the MetaLeX and issuer recipients, `metalexFeeBps` / `issuerFeeBps` (their sum capped at 10 000 bps), and an `enabled` flag.
* Registers `beforeSwap` / `afterSwap` permissions with return deltas and handles **all four swap flows**: exact-input swaps are charged in `beforeSwap` and exact-output swaps in `afterSwap`, in both directions (`zeroForOne` and `oneForZero`).

Uniswap v4 also places requirements on the hook's address; the source documents them.


# Conditions

Condition contracts that gate state transitions, including the secondary-trading family

A **condition** is a contract that gates a state transition (issuance, scripification, de-scripification, deal close, round acceptance, secondary settlement) on an onchain check. There are two interfaces:

* the generic [`ICondition`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/interfaces/ICondition.sol), with an opaque `bytes` payload;
* the typed `ISecondaryTradingCondition` (`src/libs/conditions/BaseSecondaryTradingCondition.sol`), used on the secondary-trading path.

## The `ICondition` interface

```solidity
interface ICondition {
    function checkCondition(
        address _contract,
        bytes4 _functionSignature,
        bytes memory data
    ) external view returns (bool);
}
```

`checkCondition` receives the calling contract, the function selector being gated and ABI-encoded context, and returns whether the action may proceed. Where the protocol accepts conditions, for example `IssuanceManager.deployCyberScrip` (`certToScripConditions`, `scripToCertConditions`), `DealManager.proposeDeal` (`conditions`) and `RoundManager.submitEOI` (`conditions`), they are passed as `address[]` / `ICondition[]`.

## Built-in `ICondition` contracts

In [`src/libs/conditions/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/libs/conditions/README.md), all extending the abstract `BaseCondition` (which adds ERC-165 support):

| Condition                                  | Purpose                                                                                                                                                                                                            |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **LexChexCondition**                       | Requires a valid LeXcheX credential by wrapping `ILexChex.hasValidLexCheX`. See [LexChex](/building-on-the-protocol/reference/contracts/lexchex).                                                                  |
| **NonUSNationalityCondition**              | A zkPassport-backed check that the address is held by a non-US person (Regulation S). UUPS-upgradeable; caches proof expiry per address and supports per-manager founder overrides. See `IZKPassportVerifier`.     |
| **IssuerApprovalRecertificationCondition** | Attached as a scrip-to-LET condition, requires an issuer admin's per-investor approval (`setInvestorApproval`) before that investor converts scrip back into a Ledger Entry Token (LET) with `convertScripToCert`. |
| **OrCondition**                            | Composes child conditions with disjunctive (OR) logic.                                                                                                                                                             |

## Secondary-trading conditions

Secondary trades through the `DealManager` (`postOffer` / `acceptOffer` / `finalizeSecondaryTradeAgreement`) use a typed variant instead of the opaque `bytes` payload:

```solidity
interface ISecondaryTradingCondition is IERC165 {
    function checkCondition(
        IDealManager dealManager,
        bytes4 functionSignature,
        bytes32 offerId,
        bytes32 agreementId
    ) external view returns (bool);
}
```

Implementations resolve offer and escrow state through `IDealManager.getOffer` / `getSecondaryEscrow` with compile-time-checked arguments. Conditions are validated through ERC-165 when configured, and attached on the `DealManager` in three sets:

* **SPV threshold conditions** (`setSpvThresholdConditions(conditions)`) form the fund-specific layer applied to every trade.
* **Pathway threshold conditions** (`setPathwayThresholdConditions(pathway, conditions, enabled)`) are keyed by the trade's elected **exemption pathway**:

  ```solidity
  enum ExemptionPathway { NONE, RULE_144, SECTION_4A7, SECTION_4A1HALF, RULE_144A, REGULATION_S }
  ```

  A buy offer pins its pathway at `postOffer`; on a sell offer each buyer elects a pathway at acceptance, within any pin the seller set. Threshold conditions are re-checked once a settlement exists and at finalization.
* **Closing conditions** (`setClosingConditions(conditions)`) are evaluated at finalization for every trade, whatever its pathway.

All three setters are `onlyAdmin` on the SPV's own BorgAuth.

### Built-in secondary conditions

In [`src/libs/conditions/secondary/`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/src/libs/conditions/secondary/README.md), extending `SecondaryTradingConditionBase`. Most are shared singletons configured per SPV (cyberCORP), with configuration gated by that SPV's own BorgAuth admin. `script/deploy-secondary-conditions.s.sol` deploys one instance of each per chain; [Deployments](/building-on-the-protocol/reference/deployments) lists the addresses.

| Condition                               | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **EligibilityCondition**                | Both parties must be admin-cleared to trade. The catch-all for offchain eligibility checks.                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| **HoldingPeriodCondition**              | Rule 144 holding-period verification. Anchors on the LET contract's base `acquisitionTimestamp(tokenId)` (no record fails closed, so backfill imported LETs), moved earlier only by the fund-interest extension's `tackedFromAcquisitionDate` when 144(d)(3) tacking is asserted.                                                                                                                                                                                                                                                         |
| **Rule144DisclosureCondition**          | Rule 144(c)(2) current-public-information gate with a freshness policy.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| **Section4a7DisclosureCondition**       | §4(a)(7) information-delivery gate.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| **LegalOpinionCondition**               | §4(a)(1½) GP or issuer-counsel assurance gate.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| **RegSDistributionComplianceCondition** | Regulation S distribution compliance period, per issuer category.                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| **HolderCapCondition**                  | ICA §3(c)(1) / §3(c)(1)(C) / §3(c)(7) holder limits, counted at acceptance and finalization.                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| **CFIUSCondition**                      | FIRRMA gating for CFIUS-sensitive SPVs, including a blocked-jurisdictions and blocked-affiliation list that reaches foreign control.                                                                                                                                                                                                                                                                                                                                                                                                      |
| **USStateOfResidenceCondition**         | Blue-sky state gating for U.S. acceptors (blocked-states list; unregistered SPVs block NY by default).                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **LexChexBadgeKindCondition**           | Parameterizable investor-status gate on the LeXcheXBadge, the badge-based counterpart of `LexChexCondition` for secondary trading. Enforces status fact-keys (which follow the party anywhere) or exactly one per-SPV entitlement (which counts only for the offer's SPV). `updateIssuers` optionally restricts whose credentials count (empty accepts any issuer). Name the operator when gating on `K_SYNDICATE`, since a shared badge lets other issuers grant seats in the same SPV.                                                  |
| **LegionSoulboundCondition**            | Issuer circle gate: a seat *plus* a minimum rank ("MEMBER or better"). A qualifying credential carries `K_SYNDICATE` scoped to the SPV and its rank in `K_DATA`, on one record. The gate resolves the holder's *most recent* valid seat through `getMostRecentValidWith`, so a newer seat demotes an older one even if the older one was never voided. Per-SPV `setConfig(spv, minTier, applyToSeller)` (by the SPV's BorgAuth admin); platform-level `updateIssuers` names whose seats count. For membership alone, set the lowest rung. |
| **GPLPApprovalCondition**               | Per-deal GP/LP manual approval gate.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| **KillSwitchCondition**                 | Finalization kill switch held jointly by two independent admins (closing condition), platform-wide or per settlement. Either admin can raise it; lowering it takes a proposal from one admin and confirmation by the other. A proposal from a key that has since been rotated out of its admin slot cannot be confirmed (`ProposerNotAdmin`), so one admin cannot propose, rotate and confirm alone.                                                                                                                                      |
| **TimeSettlementPeriodCondition**       | Minimum delay between acceptance and finalization (default 24 h, per-DealManager overrides). A closing condition an admin must opt into with `setClosingConditions`; nothing installs it by default.                                                                                                                                                                                                                                                                                                                                      |

The deployed `AccreditedInvestorCondition`, `QualifiedPurchaserCondition`, `QualifiedInstitutionalBuyerCondition`, `NonUSPersonCondition`, `SpvWhitelistCondition` and `SyndicateCondition` are `LexChexBadgeKindCondition` instances, one per fact-key.

The credential-reading conditions inherit `BadgeScopedCondition`, which selects the credential registry that judges an SPV's parties (a platform default plus per-SPV overrides). Shared jurisdiction-classification logic lives in `src/libs/policies/`: `USJurisdictionPolicy`, and `LookThroughPolicy` for the ICA §3(c)(1)(A) U.S.-investor look-through.

## Custom conditions

Any contract implementing the relevant interface works. Because `checkCondition` receives the calling contract, the selector and context, a condition can test any onchain state (a token balance, a credential, an oracle reading, a governance outcome) and be attached wherever the protocol accepts a condition list. Secondary-trading conditions must advertise `ISecondaryTradingCondition` through ERC-165 to be accepted at configuration time.

This page specifies the interfaces. Check each built-in condition's constructor, configuration functions and behavior against its source.


# Access control (BorgAuth)

BorgAuth numeric authority levels and who can call what

Every access check in the protocol runs through **BorgAuth**, one ACL contract per cyberCORP. BorgAuth uses **numeric role levels in a hierarchy** instead of named roles.

* **Source:** [`src/libs/auth.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/libs/auth.sol)

## The role model

A role is a `uint256`, and higher numbers outrank lower ones. `onlyRole(role)` **passes when `userRoles[user] >= role`**. It is a threshold check; `matchRole` is the exact-match variant.

### Built-in role constants

| Constant          | Value |
| ----------------- | ----- |
| `OWNER_ROLE`      | `99`  |
| `ADMIN_ROLE`      | `98`  |
| `PRIVILEGED_ROLE` | `97`  |

### Roles the protocol assigns

The `CyberCorpFactory` and `CyberCorp` set these levels when a cyberCORP is deployed:

| Holder                                                                      | Level               | Why                                                                                                                                                                                          |
| --------------------------------------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Company officer (`CompanyOfficer.eoa`)                                      | `200`               | Set by `CyberCorp.addOfficer` / `updateOfficer` and by the factory, never lowering a higher custom level; removal zeroes only an exact `200`. `200 ≥ 99`, so officers also pass `onlyOwner`. |
| `CyberCorp` contract                                                        | `200`               | So the corp can act on its own auth.                                                                                                                                                         |
| `IssuanceManager`, `DealManager`, `RoundManager`                            | `99` (`OWNER_ROLE`) | So the manager contracts can call owner-gated functions on the suite.                                                                                                                        |
| The deploying factory (`CyberCorpFactory`, or the specialised factory used) | `99` (`OWNER_ROLE`) | The BorgAuth constructor makes its deployer the owner, and the factory does not renounce the level after deployment.                                                                         |

The factory's level is a standing owner grant from the company to a contract MetaLeX can upgrade. Some companies, v5 companies among them, still grant it, and others do not. Check `userRoles(<factory address>)` on your company's BorgAuth; an owner can remove the grant with `updateRole(factory, 0)`.

Authority is the numeric level a function requires. BorgAuth defines no named roles such as `ISSUER_AUTHORITY` or `OFFICER_AUTHORITY`, and `200` is the level a human officer holds.

## BorgAuth functions

```solidity
uint256 public constant OWNER_ROLE = 99;
uint256 public constant ADMIN_ROLE = 98;
uint256 public constant PRIVILEGED_ROLE = 97;

mapping(address => uint256) public userRoles;
mapping(uint256 => address) public roleAdapters;

function updateRole(address user, uint256 role) external;       // caller must hold OWNER_ROLE
function initTransferOwnership(address newOwner) external;      // caller must hold OWNER_ROLE
function acceptOwnership() external;                            // caller must be pendingOwner
function zeroOwner() external;                                  // caller renounces to role 0
function setRoleAdapter(uint256 role, address adapter) external;// caller must hold OWNER_ROLE
function onlyRole(uint256 role, address user) public view;      // reverts if under-authorized
function matchRole(uint256 role, address user) public view;     // reverts unless exactly equal
```

* **`updateRole`** is the single grant and revoke entry point. It sets a user's level up or down and requires `OWNER_ROLE`.
* **`zeroOwner`** renounces the owner's own level to `0`, permanently removing its admin control from contracts using this auth.
* **Role adapters.** `setRoleAdapter` plugs in an `IAuthAdapter`, so custom logic (a credential check, for example) can satisfy a role instead of, or as well as, a stored level.
* **Ownership transfer** takes two steps: `initTransferOwnership`, then `acceptOwnership`.

## Using BorgAuth in a contract: `BorgAuthACL`

Protocol contracts inherit `BorgAuthACL`, which holds the `AUTH` reference and provides modifiers:

| Modifier       | Passes when the caller's level is… |
| -------------- | ---------------------------------- |
| `onlyOwner`    | `≥ OWNER_ROLE` (99)                |
| `onlyAdmin`    | `≥ ADMIN_ROLE` (98)                |
| `onlyPriv`     | `≥ PRIVILEGED_ROLE` (97)           |
| `onlyRole(n)`  | `≥ n`                              |
| `matchRole(n)` | exactly `n`                        |

Example: `CyberCorp.addEscrowedOfficerSignature` is `onlyRole(200)`, so only an officer (level 200) can call it; most other `CyberCorp` functions are `onlyOwner` (level 99 or above).

## `onlyIssuanceManager` and the token contracts

`onlyIssuanceManager` checks that `msg.sender` *is* the IssuanceManager contract; it is not a BorgAuth role check. End users act through the IssuanceManager, which BorgAuth authorizes. `CyberShares` gates its mint, burn and configuration functions this way.

`CyberScrip` has **two** surfaces:

* **Strictly `onlyIssuanceManager`:** `mint`, `burnFrom` and `forceBurn`. Force burn also moves vault backing, so admins use `IssuanceManager.forceScripBurn`.
* **`onlyIssuanceManagerOrAdmin`:** `setRestrictionHook`, `setFrozen`, `forceTransfer`, `setMaxHolderCount` and the three one-way `disable*` switches, callable directly by a BorgAuth `ADMIN_ROLE` (98) holder. On v4 scrip, `setMaxHolderCount` is `onlyIssuanceManager` and the v4 IssuanceManager has no wrapper for it, so a v4 company cannot set a holder cap.

`LedgerEntryToken`, the LET contract, also has **two** surfaces:

* **Strictly `onlyIssuanceManager`:** minting and assignment (`safeMint`, both `safeMintAndAssign` overloads, `safeMintFromAndAssign`, `assignCert`), `updateCertificateDetails`, `setExtension` and `updateIssuanceManager`.
* **`onlyIssuanceManagerOrAdmin`:** the administrative surface, callable directly on the LET contract by a BorgAuth `ADMIN_ROLE` (98) holder (the modifier resolves the IssuanceManager's `AUTH()` and checks the role): restriction hooks, the delivery and registration transferability switches, default and per-LET legends, `voidCert` / `unvoidCert`, `addIssuerSignature` and `endorseCertificate`, issue and acquisition timestamps (and the tacking anchor), reserved units, `setSeriesData`, the look-through badge, and `initializeHolderCount`.


# Upgrade model

The company-owned contracts use UUPS proxies, with beacon proxies for `LedgerEntryToken` (LET contract) and `CyberScrip` instances, and **ERC-7201 namespaced storage**. An upgrade needs both MetaLeX and the company: MetaLeX publishes the implementation, and the company's owner installs it (**co-approval**).

## Co-approval

* MetaLeX publishes new implementations to the factories (`*Factory.setRefImplementation`, `setCyberCertPrinterRefImplementation`, `setCyberScripRefImplementation`).
* The cyberCORP's owner opts in separately by calling `upgradeToAndCall(published, "")` on each of its own UUPS proxies and running the two beacon upgrades on its IssuanceManager. "Owner" means any address at BorgAuth level 99 or above on the company's BorgAuth, which includes the company's officers (level 200). Each contract's `_authorizeUpgrade` is `onlyOwner` **and** requires the new implementation to equal the published `getRefImplementation()` of the factory recorded in that contract (`upgradeFactory` / `getUpgradeFactory()`); otherwise it reverts `NotRefImplementation`.
* An issuer cannot upgrade to an implementation MetaLeX has not published, and the factories have no function that upgrades a company's contracts.

Implementation contracts call `_disableInitializers()` in their constructors, so only the proxies are ever initialized.

{% hint style="warning" %}
The factory that deploys a company is the initial owner of the company's BorgAuth (level 99) and does not renounce that level. The factory is itself an upgradeable proxy that MetaLeX controls. Read `userRoles(<factory>)` on the company's BorgAuth to see whether a company still grants it; see [Access control](/building-on-the-protocol/reference/access-control#roles-the-protocol-assigns).
{% endhint %}

[Upgrades and what MetaLeX controls](/how-cybercorps-works/upgrades-and-control) explains what this model lets MetaLeX do and not do.

## Versions in production

Each company-owned contract reports a `DEPLOY_VERSION`. The source is at `"5"`, and the factories on Ethereum, Base and Arbitrum (and on the Base Sepolia and Ethereum Sepolia testnets) publish v5 reference implementations, so every new company is a v5 company.

An existing company keeps the version it was deployed with until its owner upgrades it. v4 companies run `"4"` (IssuanceManager `"4.1"`, DealManager `"4.0.1"`), and older ones `"3"`. Integrations must read `DEPLOY_VERSION()` on the specific contract and pick the matching ABI.

A company upgrades **all six** of its contracts together: the CyberCorp, IssuanceManager, DealManager and RoundManager proxies, and the IssuanceManager's LedgerEntryToken and CyberScrip beacons. The contracts call each other's functions, and v4 and v5 differ in some of them: the v5 IssuanceManager lacks functions a v4 DealManager calls, and the v5 managers call LET contract functions a v4 LET contract lacks. A company with some contracts on v4 and others on v5 can therefore see deal, round and issuance calls revert. See [Upgrade a cyberCORP](/building-on-the-protocol/how-to/upgrade-a-cybercorp).

The MetaLeX-owned singletons (factories, the agreement registry, the URI builder, the certificate extensions) serve every company at once and keep the interfaces that v4 and older companies call.

## UUPS companies and legacy beacon companies

Companies deployed through the `CyberCorpFactory` stack put the CyberCorp, IssuanceManager, DealManager and RoundManager behind their own ERC-1967 (UUPS) proxies, so each cyberCORP decides when it upgrades.

The oldest cyberCORPs use beacon proxies for the CyberCorp, IssuanceManager and DealManager, pointing at beacons owned by legacy factories that MetaLeX controls. MetaLeX can move those beacons for all such companies at once; the company owner cannot, and `upgradeToAndCall` on those proxies reverts `UUPSUnauthorizedCallContext`. The legacy beacons on Ethereum, Base and Arbitrum point at `DEPLOY_VERSION` `"3"` code, and the v5 upgrade scripts do not move them.

`DealManagerWithMigration` is a one-time beacon implementation that re-points a legacy DealManager's `upgradeFactory` at the current DealManagerFactory, for its fee settings. It does not convert a beacon proxy into a UUPS proxy.

## Beacons inside a UUPS company

Even in a UUPS company, the instances an `IssuanceManager` owns (its `LedgerEntryToken` and `CyberScrip` proxies) are beacon proxies pointing at a beacon **owned by the IssuanceManager itself**, created in `IssuanceManager.initialize` and seeded from the factory's reference implementations. One call moves every LET contract (or every scrip) of the company, still gated to the factory's published reference.

## Architecture diagram

[`ownership-and-upgradeability.md`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/ownership-and-upgradeability.md) in the repository root has the full Mermaid class diagram of the UUPS and legacy paths.

## See also

* [Upgrade a cyberCORP](/building-on-the-protocol/how-to/upgrade-a-cybercorp)
* [Upgrades and what MetaLeX controls](/how-cybercorps-works/upgrades-and-control)


# Agreement templates

Reusable legal-instrument templates live in the [`/templates`](https://github.com/MetaLex-Tech/cybercorps-contracts/tree/develop/templates/README.md) directory. Registration in `CyberAgreementRegistry` is per chain, so a template can exist on one chain and not another: the cyberSTOCK template below is registered on Ethereum and Base Sepolia but not on Base or Arbitrum. Call `getTemplateDetails(templateId)` on the target chain before relying on a template; it reverts `TemplateDoesNotExist` for an unregistered id.

## cyberSAFE

| Template                                                  | Style                | Filing path         |
| --------------------------------------------------------- | -------------------- | ------------------- |
| `MetaLeX cyberSAFE US style Reg D v 1.0.md`               | YC post-money        | Reg D (US)          |
| `MetaLeX cyberSAFE UK Cayman style Reg S v 1.0.md`        | YC post-money        | Reg S (UK / Cayman) |
| `MetaLeX cyberSAFE jx-neutral-style Reg D raise v 1.0.md` | Jurisdiction-neutral | Reg D (US)          |
| `MetaLeX cyberSAFE jx-neutral-style Reg S raise v 1.0.md` | Jurisdiction-neutral | Reg S               |
| `mlx_safe_reg_d_v1_3.md` / `mlx_safe_reg_s_v1_3.md`       | v1.3 series          | Reg D / Reg S       |

## cyberSAFT

| Template                                            | Filing path |
| --------------------------------------------------- | ----------- |
| `MetaLeX cyberSAFT reg D v.1.0.md`                  | Reg D       |
| `MetaLeX cyberSAFT Reg S raise v 1.0.md`            | Reg S       |
| `mlx_saft_reg_d_v1_3.md` / `mlx_saft_reg_s_v1_3.md` | v1.3 series |

## cyberSAFTE

* `mlx_safte_reg_d_v1_3.md`: Reg D, v1.3.

## cyberSTOCK

* `mlx_cyberstock_reg_d_v1_0.md`: a tokenized-share subscription agreement, Reg D, v1.0, with template id `bytes32(bytes("metalex_cyberstock_reg_d_v1_0"))`.

## cyberTokenWarrant

| Template                                                                      | Filing path              |
| ----------------------------------------------------------------------------- | ------------------------ |
| `MetaLeX cyberTokenWarrant a16z US style reg D v 1.0.md`                      | a16z-derived, Reg D (US) |
| `MetaLeX cyberTokenWarrant a16z jx-neutral-style-issuer Reg D raise v 1.0.md` | jx-neutral, Reg D        |
| `MetaLeX cyberTokenWarrant a16z-jx-neutral-style-issuer Reg S raise v 1.0.md` | jx-neutral, Reg S        |
| `MetaLeX cyberTokenWarrant non-US Reg S v 1.0.md`                             | Reg S                    |
| `mlx_safe_tw_reg_d_v1_3.md` / `mlx_safe_tw_reg_s_v1_3.md`                     | v1.3 series              |

## MetaDAO Futarchy Governance SPC

* `MetaDAO Futarchy Governance SPC - Board Consent - Approval of SegCo v 1.0.md`
* `MetaDAO Futarchy Governance SPC - SegCo combined v 1.0.md`

## LeXcheX agreement

* `MetaLeX LeXCheX Agreement.md`, which a credential subject countersigns on receiving a LeXcheX credential.

## Custom templates

Anyone can register a template with `CyberAgreementRegistry.createTemplate(templateId, title, legalContractUri, globalFields, partyFields)`. Template ids are caller-chosen `bytes32` values (by convention, `bytes32(bytes("<name>"))`) and must be unused, and a template's fields cannot be overwritten once created. The directory also holds a combined cyberSAFE and cyberTokenWarrant template prepared for one issuer, as an example of a custom variant.

Standalone agreements (`createStandaloneContractAndSign`) skip this step. The registry derives the template id from `keccak256(abi.encode(title, legalContractUri, globalFields, partyFields))` and registers the template on first use.

The two `DEPRECATED-*` files in the directory (`SAFE-version-0-1`, `SAFEplusT-version-0-1`) are not for new agreements.

## See also

* [Sign a cyberAgreement](/building-on-the-protocol/how-to/sign-a-cyberagreement)
* [`CyberAgreementRegistry`](/building-on-the-protocol/reference/contracts/cyberagreementregistry)


# Security types

The `SecurityClass` enum in [`src/CyberCorpConstants.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/src/CyberCorpConstants.sol) lists the instrument types the protocol can issue. Each LET contract (a `LedgerEntryToken` deployment) is created for one `SecurityClass` and one `SecuritySeries`.

## `SecurityClass`

```solidity
enum SecurityClass {
    SAFE,
    SAFT,
    SAFTE,
    TokenPurchaseAgreement,
    TokenWarrant,
    ConvertibleNote,
    CommonStock,
    StockOption,
    PreferredStock,
    RestrictedStockPurchaseAgreement,
    RestrictedStockUnit,
    RestrictedTokenPurchaseAgreement,
    RestrictedTokenUnit
}
```

## `SecuritySeries`

```solidity
enum SecuritySeries {
    SeriesPreSeed,
    SeriesSeed,
    SeriesA,
    SeriesB,
    SeriesC,
    SeriesD,
    SeriesE,
    SeriesF,
    NA,
    ACE
}
```

`NA` marks a security with no series; `ACE` marks securities issued through an ACE offering.

## `SecurityStatus`

```solidity
enum SecurityStatus { Unassigned, Assigned, Void }
```

The status of a Ledger Entry Token (LET). `LedgerEntryToken.voidCert` sets `Void`, and the IssuanceManager calls it when it voids an emptied LET (`voidEmptyCerts`, or a seller's LET fully sold in a secondary trade). A void LET keeps its holder and units but cannot be registered to a new holder.

## Security classes (`SecurityClassInfo`)

Class-level designations for LETs are registered on the `IssuanceManager`:

```solidity
struct SecurityClassInfo {
    SecurityClass classType;
    string documentURI;    // class-level governing document
    address dataExtension; // optional ICertificateExtension-style decoder/renderer
    bytes classData;       // extensionData-style opaque payload
}
```

`IssuanceManager.defineSecurityClass` (`onlyOwner`) registers a class. Each `SecurityClass` value has at most one class, and several LET contracts can share it. The [IssuanceManager](/building-on-the-protocol/reference/contracts/issuancemanager#security-class-registry) page covers class ids, assignment and updates. `ShareExtensionV3` reads `classData` as the class-wide layer of every LET in a share class; see [Certificate extensions](/building-on-the-protocol/reference/extensions).

## Instrument-specific metadata

Each security class is paired with a [certificate extension](/building-on-the-protocol/reference/extensions) that encodes its instrument-specific terms. `CyberCorpConstants.sol` also defines supporting enums for token-warrant and vesting instruments: `ExercisePriceMethod`, `TokenCalculationMethod`, `UnlockStartTimeType`, `UnlockingIntervalType`.


# Deployments

Deployed addresses and versions per chain

Canonical contract addresses, by chain.

> **Source of truth:** the [`script/libs/DeploymentConstants.sol`](https://github.com/MetaLex-Tech/cybercorps-contracts/blob/develop/script/libs/DeploymentConstants.sol) library (and the raw deploy logs in `script/res/deployment-addresses.md`) in the contracts repository, plus MetaLeX release notes on [Substack](https://metalex.substack.com/). Most entries are proxies whose implementations differ by chain, so read the EIP-1967 implementation slot instead of assuming one.

## Production chains (Ethereum, Base, Arbitrum)

The core suite is deployed at the same addresses on Ethereum mainnet, Base and Arbitrum One (`DeploymentConstants.coreV2`):

| Contract                                 | Address                                      | Chains         |
| ---------------------------------------- | -------------------------------------------- | -------------- |
| MetaLeX Safe (multisig)                  | `0x68Ab3F79622cBe74C9683aA54D7E1BBdCAE8003C` | all three      |
| `BorgAuth` (MetaLeX platform auth)       | `0x033012a1eDA6e2E00D12CD37c5b63B9440ef5E01` | all three      |
| `CyberCorpFactory`                       | `0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2` | all three      |
| `CyberCorpSingleFactory`                 | `0xBE0D3D13AA07501beAC9b72dE9e9292E66C7A5C4` | all three      |
| `IssuanceManagerFactory`                 | `0xD353972D7955F421d94d0eA8c42c88c417F7155A` | all three      |
| `DealManagerFactory`                     | `0x3982b078f2ac306219c9540Ebc908360a960C251` | all three      |
| `RoundManagerFactory`                    | `0xc9d5d0DeDD124f9351E5880469f25AB41869aeb9` | all three      |
| `CyberAgreementRegistry`                 | `0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134` | all three      |
| `CertificateUriBuilder`                  | `0x5500c095ea7dE6F8a5E15949e24B80604cc670A3` | all three      |
| LeXcheX `BorgAuth`                       | `0xeAdeaD5C4A6747D4959489742c143bCDb95a01c2` | all three      |
| `LeXcheX`                                | `0xc8db0c3f47656aee725b0AD1835F9A3FbD0a0b62` | all three      |
| `LeXcheXMinter`                          | `0x0dD1a2a89eC172ac322B6a7a6c869180CBD0F960` | all three      |
| `LexChexCondition`                       | `0x4a08547d57C8d01e59bA8F884aB90CEe0d6d5b42` | all three      |
| `LeXcheXBadge`                           | `0x114664773Ba721a6AA43890d5FDE7939aF37618F` | all three      |
| `LeXcheXBadge` BorgAuth                  | `0x197333Fc7A828e623fbfcF88eCdc976136F0cf1d` | all three      |
| `NonUSNationalityCondition` (zkPassport) | `0xe71fE689bFAA4939A760EDF7e07f44372a43932A` | Ethereum, Base |
| `ParentCoFactory`                        | `0x5c6D411600774c8fE1Aa805d78F03202d7FCD47F` | Ethereum       |
| `PumpCorpFactory`                        | `0xe73Ea052c2891cE1668742142a6634Df09c88512` | Base           |
| `MetaDAOFactory`                         | *see latest deployment script*               |                |

The certificate image builder is a separate contract on each chain; read its address from `CertificateUriBuilder.imageBuilder()`. ACE runs in production on Base (see [ace.metalex.tech](https://ace.metalex.tech)), with its own pump stack beside the core suite.

zkSync Era is not in these tables: none of the shared addresses has code there, and v5 is not deployed there. Its agreement registry, `0x07E0a0BeC742f90f7879830bC917E783dA6a6357`, uses the signature type without `signer` and the four-field agreement id (see [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry#data-model)).

## Test environments

Base Sepolia and Ethereum Sepolia are the test environments (some tests fork them: `forge test --via-ir --fork-url <rpc>`). The testnet suites share the production addresses **except**:

| Contract                    | Ethereum Sepolia                                  | Base Sepolia                                      |
| --------------------------- | ------------------------------------------------- | ------------------------------------------------- |
| MetaLeX Safe                | *no code (testnets keep roles with the deployer)* | *no code (testnets keep roles with the deployer)* |
| `IssuanceManagerFactory`    | *as production*                                   | `0xbbD386D237f3b407E6511A52488850b1Da0cCad2`      |
| `RoundManagerFactory`       | *as production*                                   | `0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34`      |
| `NonUSNationalityCondition` | `0xd91a24Ac7D2981c6d660EDEe05Aec22eA5B95E95`      | *not deployed (no zkPassport verifier)*           |
| `ParentCoFactory`           | `0x0c6Fc81BEd7f91f7a3b3594CCc66484893634Bf9`      | `0xC1304898FAfF45cA2B07C0f4E10B77843eD5a47B`      |

## Certificate extensions

The extension proxies have the same addresses on every chain above. A v5 company points its new LET contracts at the V3 extensions, which render the whole certificate; an existing LET contract stays on the V1 or V2 proxy it was created with. See [Certificate extensions](/building-on-the-protocol/reference/extensions).

| Contract                                                   | Address                                      | Chains |
| ---------------------------------------------------------- | -------------------------------------------- | ------ |
| `SAFEExtension`                                            | `0xB2E732d29b89ec36a8Dd23CFD32901056b6579C8` | all    |
| `SAFEExtensionV3`                                          | `0x740003076c9F16c4a364AE07f3770FB77899299b` | all    |
| `ACESAFEExtension`                                         | `0x6aDaef2B79FD1cbA130c5807B31DE435FEa58EAC` | Base   |
| `ACESAFEExtensionV3`                                       | `0x9a8ef946F18C4296e50f8DF0C97b5Cf5d1b5eCD2` | all    |
| `SAFTExtension`                                            | `0x109D2A13932bE393011835B308F81ce1992E365B` | all    |
| `SAFTExtensionV2`                                          | `0x37c2A0e801e569e01f0972186aF4DB01409e92c1` | all    |
| `SAFTExtensionV3`                                          | `0x2Bf1b2f8f6009c5524886243CE4F2EF34e3d4957` | all    |
| `SAFTEExtension`                                           | `0xE070eDA75695bE3ED4B9ec3719b76Dd36794787C` | all    |
| `SAFTEExtension` (older build, used by one SAFTE template) | `0xC23fFF0B06aE5EBea862611F489b1329108ce603` | all    |
| `SAFTEExtensionV2`                                         | `0x4Acdc8618BF2C3d760a357ec11A8290c79f5b41A` | all    |
| `SAFTEExtensionV3`                                         | `0xE79C2b4a35b2509b27686D025FB8Fbab2Ca49406` | all    |
| `TokenWarrantExtension`                                    | `0xbad0b411C37cfF66e4C0B7764Db2d499eA757bb4` | all    |
| `TokenWarrantExtensionV2`                                  | `0xF5A9984DfcA4D6Dd55D6151F0cd2F4Af9522BC8F` | all    |
| `TokenWarrantExtensionV3`                                  | `0x3Bdb711517eEbd6A88D9Aa30B141adC43BAB033e` | all    |
| `ShareExtension`                                           | `0x80e8205b74e3E9882C3C57aA0b36cD465E7A4b81` | all    |
| `ShareExtensionV3`                                         | `0xa21C434379CC44bfeD6e3c1342e49951804ec30f` | all    |
| `FundInterestExtensionV3`                                  | `0x2322D1dcC199d7A9Ee60F331cA9D76d244F09a15` | all    |

"All" means Ethereum, Base, Arbitrum, Base Sepolia and Ethereum Sepolia.

## Secondary-trading conditions

The 19 shared conditions have the same addresses on Ethereum, Base, Arbitrum, Base Sepolia and Ethereum Sepolia. Each one is configured per SPV through that SPV's own BorgAuth admin; see [Conditions](/building-on-the-protocol/reference/conditions).

| Contract                               | Address                                      |
| -------------------------------------- | -------------------------------------------- |
| `EligibilityCondition`                 | `0x64f43CEfc89279aB6a529eb4C7432cF4b37f3E4B` |
| `USStateOfResidenceCondition`          | `0x1d4918a83E1F971317f0DD529f088B208Ec24d8C` |
| `LegionSoulboundCondition`             | `0x3b96b3a385009B4D0C523DfD6D5B4cf5365e2A04` |
| `HolderCapCondition`                   | `0xA8422D5fFE6b697152aEE19d13910302dE1E0576` |
| `CFIUSCondition`                       | `0x89Fa72549b5Fadf209377d5348C30bca4c7e0462` |
| `Section4a7DisclosureCondition`        | `0x42c9e2dadE9669c725D88b78cD0e545FD9e7F723` |
| `Rule144DisclosureCondition`           | `0x7e30Ef03A0C4C7094D464b714E2480EF30088629` |
| `HoldingPeriodCondition`               | `0xc5417d7DDE94310Ba334e31517deFED0aa0ED81F` |
| `LegalOpinionCondition`                | `0xD27317AED94BE6d310eA6B8c4e08360957ff24e3` |
| `RegSDistributionComplianceCondition`  | `0xe8A325Ba2dF0ba3E08A55B3E3F2bBBBc9354CebB` |
| `GPLPApprovalCondition`                | `0x4be9Eaff732F4BA3f1Fc6359a2F0858F70637c69` |
| `AccreditedInvestorCondition`          | `0xedac303CAfdedd92aB6fbECFafFC0a648d9157a1` |
| `QualifiedPurchaserCondition`          | `0xE4bcf1c4EA266bbD7F3c58A3aa02c43B5cdc9875` |
| `QualifiedInstitutionalBuyerCondition` | `0x8A617187aF27a98c89548dfFd886EEab0De0936F` |
| `NonUSPersonCondition`                 | `0xe2AAc5187058a5c97Da7e6D3D6F9D5811124dcBb` |
| `SpvWhitelistCondition`                | `0x0f2952dcf0279782e6048D2800ee3BeAd9f59f33` |
| `SyndicateCondition`                   | `0x089b4Dba2E3EB49f875c0D7F92A06276B204870f` |
| `KillSwitchCondition`                  | `0x456EDAfB7283dB8FDa2A5b3ecE576d9292512435` |
| `TimeSettlementPeriodCondition`        | `0x1E466EcbBC41f6777c4458F8880E62dBf1D08fAd` |

The six conditions from `AccreditedInvestorCondition` to `SyndicateCondition` are parameterizations of `LexChexBadgeKindCondition`: one proxy per fact-key, all on one implementation. `KillSwitchCondition` and `TimeSettlementPeriodCondition` are plain (non-upgradeable) contracts.

## Deploy versions

`CyberCorp`, `IssuanceManager`, `DealManager`, `RoundManager`, `CyberScrip` and `LedgerEntryToken` report `DEPLOY_VERSION` `"5"` in the source. On all five chains the factories publish v5 reference implementations, so new companies are v5. An existing company keeps its version (`"3"`, `"4"`, or `"4.1"` for the IssuanceManager) until its owner runs the [upgrade](/building-on-the-protocol/how-to/upgrade-a-cybercorp).

The v5 reference implementations on the production chains (each factory's `getRefImplementation()`, and the IssuanceManagerFactory's `LedgerEntryToken` and `CyberScrip` references):

| Contract           | Ethereum                                     | Base                                         | Arbitrum                                     |
| ------------------ | -------------------------------------------- | -------------------------------------------- | -------------------------------------------- |
| `CyberCorp`        | `0x511C9A7c13076f37659F15202781D802EcaeEDBb` | `0x1d85C3932ee70c74B331c80cc4a6a0fd5fFeE4E2` | `0xE4c397CE002a53d7151dD53ddaC8eba98Fea559C` |
| `IssuanceManager`  | `0xE4c397CE002a53d7151dD53ddaC8eba98Fea559C` | `0x0BAF2f3cA8361C2AcdDc9c93018049dc2099634f` | `0x40B16aCE1f9A802207A9b4B0d2918A007edDf0f5` |
| `LedgerEntryToken` | `0xd96dB07756a4EFc9aF634Ed89aFf43A29B9E3533` | `0x2e2e7A233C842F5c454bb3db69380647421B98fA` | `0x571f1b093997f32Fef6b6B055Fe9202680074E20` |
| `CyberScrip`       | `0x40B16aCE1f9A802207A9b4B0d2918A007edDf0f5` | `0x3C0E4897A6ABfA013f40E5E6D27f852A5Cc60A80` | `0x37aa6934e735f3984D4885545A080233DBc9BB75` |
| `DealManager`      | `0x37aa6934e735f3984D4885545A080233DBc9BB75` | `0x820d6D00A90185d702B2d45840690DC96F9DAEb4` | `0xb87742F0743949EA1cBe0d2427CB8D41101EEBAD` |
| `RoundManager`     | `0xc4099c6212cc28EA28c9dAD8F3dAc613372f6e34` | `0xE463CFF49e2be5c4A4820939f8243daE480c6537` | `0x511C9A7c13076f37659F15202781D802EcaeEDBb` |

The same address can hold different contracts on different chains (`0x511C…` is the CyberCorp reference on Ethereum and the RoundManager reference on Arbitrum, for example), so always pair an address with its chain.

## MetaLeX's own cyberCORP

MetaLeX runs its own Delaware C-corp on the protocol and keeps its stock ledger natively onchain in this contract suite.


# Glossary

| Term                                 | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **ACE (Asset Conversion to Equity)** | MetaLeX's product for converting a token community into equity stakeholders, at [ace.metalex.tech](https://ace.metalex.tech). Built on `PumpCorpFactory` and `ACESAFEExtension`. See [ACE](/using-the-apps/ace).                                                                                                                                                                                                                                                                                    |
| **Agreement id**                     | The `bytes32` id of an agreement in `CyberAgreementRegistry`: a deal, an EOI, a secondary settlement, or a standalone cyberSign agreement. On Ethereum, Base and Arbitrum the registry derives it from the template id, salt, global values, parties, secret hash and finalizer; the zkSync Era registry uses only the first four. See [CyberAgreementRegistry](/building-on-the-protocol/reference/contracts/cyberagreementregistry).                                                              |
| **BorgAuth**                         | MetaLeX's role-based access control framework. Each cyberCORP has its own BorgAuth with numeric role levels. See [Access control](/building-on-the-protocol/reference/access-control) and [borg-core](https://github.com/MetaLex-Tech/borg-core).                                                                                                                                                                                                                                                   |
| **Cap table**                        | The ownership record the cyberCORPs app assembles, displays and exports: every position from every source (offchain entries, LETs, scrip) in one view. It is an app record. Whether it, the chain or another record is the company's *securities ledger* depends on the company's governing documents. See [The cap table](/using-the-apps/company/captable).                                                                                                                                       |
| **Constitutive tokenization**        | Token issuance where the entity's governing documents designate the onchain record as the official register, so the chain *is* the register. Contrast with pointer tokenization.                                                                                                                                                                                                                                                                                                                    |
| **cyberCORP**                        | An onchain legal entity that issues legally constitutive digital securities through this protocol.                                                                                                                                                                                                                                                                                                                                                                                                  |
| **cyberCORPs app**                   | MetaLeX's issuer-facing app at `cybercorps.metalex.tech`. A company's sidebar sections are **mission control**, **Incorporation Hub**, **Documents**, **boardRoom**, **capTable**, **grants**, **cyberRaise**, **Tokenization Hub** and **cyberSign**. See [Run your company](/using-the-apps/company).                                                                                                                                                                                             |
| **cyberRAISE**                       | Onchain primary fundraising, built on `RoundManager`, `DealManager` and the LeXscroWLite escrow (`LexScrowStorage`). The cyberCORPs app's sidebar entry for it reads **cyberRaise**. See [cyberRAISE](/using-the-apps/cyberraise).                                                                                                                                                                                                                                                                  |
| **cyberSign**                        | MetaLeX's app for signing and executing legal agreements, built on `CyberAgreementRegistry`. See [cyberSign](/using-the-apps/cybersign).                                                                                                                                                                                                                                                                                                                                                            |
| **cyberTRADE**                       | Settlement for negotiated secondary trades of private securities, through the `DealManager` offer and acceptance flow (with an elected exemption pathway) and the LeXscroWLite escrow.                                                                                                                                                                                                                                                                                                              |
| **DGCL**                             | Delaware General Corporation Law, the most fully worked-out statutory reference for the protocol.                                                                                                                                                                                                                                                                                                                                                                                                   |
| **Documents**                        | The cyberCORPs app section that collects a company's formation mail, notices and filings, with links to the boardRoom's governance documents and to cyberSign. See [Run your company](/using-the-apps/company).                                                                                                                                                                                                                                                                                     |
| **Endorsement**                      | A signed record on a LET naming an endorser and an endorsee, optionally bound to an agreement registry and agreement id. An issuance endorsement names the first holder. Title moves to a new holder of record only on delivery to the endorsee of the latest live endorsement. See [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken).                                                                                                                             |
| **EOI (Expression of Interest)**     | An EIP-712-signed message in which an investor declares intent to take part in a cyberRAISE round on stated terms.                                                                                                                                                                                                                                                                                                                                                                                  |
| **Exemption pathway**                | The securities-law exemption a secondary trade settles under (`RULE_144`, `SECTION_4A7`, `SECTION_4A1HALF`, `RULE_144A` or `REGULATION_S`), elected per trade and enforced by per-pathway [condition](/building-on-the-protocol/reference/conditions) sets on the `DealManager`.                                                                                                                                                                                                                    |
| **Holder of record**                 | The address registered as a LET's legal owner (`legalOwnerOf`). It can differ from the wallet in possession of the token (`ownerOf`).                                                                                                                                                                                                                                                                                                                                                               |
| **Incorporation Hub**                | The cyberCORPs app section that holds a company's onchain formation record and founding documents, plus the private state formation record for a company formed through the app. See [Run your company](/using-the-apps/company).                                                                                                                                                                                                                                                                   |
| **Ledger Entry Token (LET)**         | An ERC-721 token minted by a LET contract. Each LET is one entry on a cyberCORP's register of holders, recording the holder of record, units and details of one position. Its onchain image renders the certificate. See [LedgerEntryToken](/building-on-the-protocol/reference/contracts/ledgerentrytoken) and [LETs and scrip](/how-cybercorps-works/lets-and-scrip).                                                                                                                             |
| **Legal terms**                      | The legal class, series and versioned terms recorded behind a security class in the cyberCORPs app. One current officer wallet approves an exact version before it activates, and the cap table and its exports read the activated version. See [The cap table](/using-the-apps/company/captable).                                                                                                                                                                                                  |
| **LET contract**                     | The `LedgerEntryToken` deployment that mints a class's or series' LETs. Each class or series has its own LET contract, created by the company's IssuanceManager (`createCertPrinter`).                                                                                                                                                                                                                                                                                                              |
| **LeXcheX**                          | MetaLeX's onchain accreditation and KYC/AML credential system: soulbound, wallet-bound NFT credentials. See [LeXcheX](/using-the-apps/lexchex).                                                                                                                                                                                                                                                                                                                                                     |
| **LeXcheXBadge**                     | The unified soulbound credential registry (LeXcheX v2, `src/creds/lexchexBadge.sol`): fact-keyed credential attributes with expiries, read by the badge-scoped secondary-trading [conditions](/building-on-the-protocol/reference/conditions).                                                                                                                                                                                                                                                      |
| **LeXscroWLite**                     | The atomic deal-closing escrow, implemented as the `LexScrowStorage` library linked into `DealManager` and `RoundManager`. See [LeXscroWLite](/building-on-the-protocol/reference/contracts/lexscrowlite).                                                                                                                                                                                                                                                                                          |
| **LiquiLeX**                         | AMM-native secondary liquidity for scrip, using Uniswap v4 pools and the `MetalexIssuerFeeHook`. See [Deploy a LiquiLeX pool](/building-on-the-protocol/how-to/deploy-liquilex-pool).                                                                                                                                                                                                                                                                                                               |
| **MetaDAO**                          | A futarchy-governed token launchpad that prescribes the legal entity a project uses: a segregated portfolio of a Cayman Islands segregated portfolio company. `MetaDAOFactory` deploys the structure, and the cyberCORPs app has a formation page for it. See [Launchpads](/using-the-apps/launchpads).                                                                                                                                                                                             |
| **mission control**                  | The cyberCORPs app's company dashboard. It shows what is true about the company now and what needs action, such as contract upgrades, formation steps, annual reports and cap table checks. See [Run your company](/using-the-apps/company).                                                                                                                                                                                                                                                        |
| **Pointer tokenization**             | Token issuance where the chain is a notification layer and the official register lives offchain. Contrast with constitutive tokenization.                                                                                                                                                                                                                                                                                                                                                           |
| **Public company page**              | A wallet-free page for every cyberCORP at `cybercorps.metalex.tech/company/{chainId}/{address}`, showing what the chain and the public indexer record about it. See [Run your company](/using-the-apps/company).                                                                                                                                                                                                                                                                                    |
| **PumpCorp**                         | A cyberCORP variant deployed by `PumpCorpFactory` for ACE.                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| **Reg D / Reg S**                    | Two exemption frameworks for private securities under the US Securities Act of 1933. Reg D is the US-investor framework (accreditation-driven); Reg S is the non-US-investor framework.                                                                                                                                                                                                                                                                                                             |
| **Scrip**                            | A fungible ERC-20 token that tracks a LET's units in a fixed ratio; a holder converts LET units into scrip and back. Its rights come from the company's governing documents and the scrip's terms; under MetaLeX-form bylaws scrip is not stock and confers no stockholder rights on its own. Each LET contract has at most one `CyberScrip` contract. See [Tokenization Hub](/using-the-apps/company/tokenization-hub) and [CyberScrip](/building-on-the-protocol/reference/contracts/cyberscrip). |
| **Scripification**                   | Converting units of a LET into scrip (`scripifyCert`). De-scripification converts scrip back into a LET (`convertScripToCert`).                                                                                                                                                                                                                                                                                                                                                                     |
| **Securities ledger**                | The company's legally definitive record of its securities. Which record that is (the chain, the app's cap table, or something else) depends on the company's governing documents, such as its bylaws; it never follows from tokenization or from where data is stored.                                                                                                                                                                                                                              |
| **SegCo**                            | A segregated portfolio of a Cayman Islands segregated portfolio company (SPC).                                                                                                                                                                                                                                                                                                                                                                                                                      |
| **Tokenization Hub**                 | The cyberCORPs app section that configures, issues and manages the company's LETs and scrip. Reference UI at [`apps/cybercorps-web`](https://github.com/MetaLex-Tech/metalex-webapp/tree/develop/apps/cybercorps-web). See [Tokenization Hub](/using-the-apps/company/tokenization-hub).                                                                                                                                                                                                            |
| **Tokenized / untokenized**          | Whether units have a token onchain (a LET or scrip). Says nothing about where a record is stored or which record is definitive.                                                                                                                                                                                                                                                                                                                                                                     |
| **Umia**                             | A token launchpad that, like MetaDAO, prescribes the legal entity a project uses: a segregated portfolio of Umia Launcher SPC. The cyberCORPs app has a password-protected formation page for it. See [Launchpads](/using-the-apps/launchpads).                                                                                                                                                                                                                                                     |
| **Unregistered scrip**               | Scrip in a holder's wallet beyond what recorded grants' withdrawals explain: beneficial units with no registered position accounting for them. The cyberCORPs app flags it for review.                                                                                                                                                                                                                                                                                                              |
| **zkPassport**                       | Privacy-preserving passport credential used by `NonUSNationalityCondition`.                                                                                                                                                                                                                                                                                                                                                                                                                         |


