SPEC 006 — Convertible Instruments
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P1 — Core Product |
| Backend | equa-server/modules/persistence/src/captable/ |
| API | equa-server/modules/api/src/endpoints/captable-endpoints.ts |
| Frontend | equa-web/src/modules/convertibles/ |
Confluence source incorporated: “Convertible Note Fully Diluted Equation” — see section 6.1a below
1. Feature Purpose
Convertible Instruments manages pre-equity financing instruments — SAFEs (Simple Agreements for Future Equity), convertible notes, SAFTs (Simple Agreements for Future Tokens), and custom instruments. Investors provide capital via these instruments, which later convert into equity shares upon a qualifying event (funding round, acquisition, or maturity). The module tracks instrument terms, calculates accrued interest, models conversion scenarios, and executes conversions that create new shareholdings on the cap table.2. Current State (Verified)
2.1 Backend
| Component | Path |
|---|---|
| Entity definitions | equa-server/modules/persistence/src/schema.ts |
| Cap table reading | equa-server/modules/persistence/src/captable/reading.ts |
| Cap table writing | equa-server/modules/persistence/src/captable/writing.ts |
| API endpoints | equa-server/modules/api/src/endpoints/captable-endpoints.ts |
2.2 Frontend
| Component | Path |
|---|---|
| Convertibles module | equa-web/src/modules/convertibles/ |
| Utility functions | equa-web/src/modules/convertibles/utility.ts |
| Type definitions | equa-web/src/modules/convertibles/types.ts |
| Cap table service | equa-web/src/service/services/captable/ |
| Route paths | equa-web/src/logic/paths.ts lines 118–126 |
3. Data Model
3.1 ConvertibleInstruments
Core instrument terms stored as content-addressed records.| Column | Type | Constraints | Description |
|---|---|---|---|
| (hash) | Hash | PK (from HashedTable) | Content-addressed identifier |
| instrumentType | ConvertibleInstrumentType | Instrument classification (see enum below) | |
| authorizedRaiseAmount | numeric | Maximum capital the instrument can raise | |
| interestRate | numeric | nullable | Annual interest rate (decimal, e.g., 0.05 for 5%) |
| accrualFrequency | smallint | nullable | Interest compounding period (1=daily, 2=monthly, 3=annual) |
| maturityDate | date | nullable | Date the instrument matures |
| valuationMax | numeric | nullable | Valuation cap (upper bound for conversion price) |
| valuationMin | numeric | nullable | Valuation floor |
| conversionDiscount | numeric | nullable | Discount rate applied at conversion (e.g., 0.20 for 20%) |
| earlyExitMultiple | numeric | nullable | Multiple returned on early exit/acquisition |
| estimatedConversionPrice | numeric | nullable | Pre-calculated estimated price per share at conversion |
| qualifiedFinancingAmount | numeric | nullable | Minimum financing amount to trigger automatic conversion |
schema.ts lines 1501–1534
3.2 ConvertibleInstrumentType Enum
9 instrument types, each identified by a UUID constant (utility.ts lines 4–14):
| Type | Description |
|---|---|
convertibleNote | Standard convertible note with interest and maturity |
startupsConvertibleNote | Startups.com variant convertible note |
ycConvertibleNote | Y Combinator convertible note template |
preMoneySafe | Pre-money SAFE (no valuation cap on existing shares) |
startupsConvertibleSecurity | Startups.com convertible security |
ycvcSafe | YC/VC SAFE template |
postMoneySafe | Post-money SAFE (valuation cap includes the SAFE amount) |
saft | Simple Agreement for Future Tokens |
custom | User-defined instrument terms |
3.3 AccrualFrequency Enum
| Value | Meaning |
|---|---|
| 1 | Daily compounding |
| 2 | Monthly compounding |
| 3 | Annual compounding |
utility.ts lines 28–32
3.4 InstrumentConversions
Records when a convertible instrument converts to equity.| Column | Type | Constraints | Description |
|---|---|---|---|
| (hash) | Hash | PK (from HashedTable) | Content-addressed identifier |
| convertibleInstrument | Hash | FK → ConvertibleInstruments | Source instrument |
| shares | numeric | Total shares issued in conversion | |
| valuation | numeric | Organization valuation used for conversion price |
schema.ts lines 1536–1546
3.5 NoteConversions
Per-note distribution details within a conversion event.| Column | Type | Constraints | Description |
|---|---|---|---|
| (hash) | Hash | PK (from HashedTable) | Content-addressed identifier |
| conversion | Hash | FK → InstrumentConversions | Parent conversion event |
| destination | Hash | FK → Holdings | Destination shareholding created |
| source | Hash | FK → Holdings | Source holding (the note) |
| sharesOwed | numeric | Calculated shares owed to this note holder | |
| value | numeric | USD value of the note at conversion |
schema.ts lines 1548–1564
3.6 Repayments
Records when a convertible instrument is repaid instead of converted.| Column | Type | Constraints | Description |
|---|---|---|---|
| (hash) | Hash | PK (from HashedTable) | Content-addressed identifier |
| convertibleInstrument | Hash | FK → ConvertibleInstruments | Repaid instrument |
| issueDate | date | Date of repayment |
schema.ts lines 1566–1573
3.7 Holdings (Relationship)
Convertible instruments are tracked as Holdings with aconvertibleInstrument field:
| Column | Type | Description |
|---|---|---|
| convertibleInstrument | Hash | nullable, FK → ConvertibleInstruments |
schema.ts lines 1417–1498 (Holdings entity, convertibleInstrument field)
Relationships
4. API Endpoints
Convertible instruments are managed through the Holdings API (shared with other holding types on the cap table).| Method | Path | Auth Guard | Description |
|---|---|---|---|
| POST | /holding | canEditCapTable | Create holding with convertibleInstrument field |
| PUT | /holding/:holding | canEditCapTable | Update holding and instrument terms |
| PATCH | /holding/:holding | canEditCapTable | Partial update |
| DELETE | /holding/:holding | canEditCapTable | Delete holding |
| POST | /holding/:holding/convert | canEditCapTable | Execute instrument conversion |
| POST | /holding/:holding/repay | canEditCapTable | Record instrument repayment |
captable-endpoints.ts lines 429–442 (convert/repay), holdings CRUD throughout file
Request Schemas
5. Frontend Components
Pages
| Component | File | Purpose |
|---|---|---|
| ListConvertibleInstrumentsPage | pages/list-convertible-instruments.tsx | Lists all instruments in a block |
| NewConvertibleInstrumentPage | pages/new-convertible-instrument.tsx | Create instrument form |
| EditConvertibleInstrumentPage | pages/edit-convertible-instrument.tsx | Edit instrument terms |
| ViewConvertibleInstrumentPage | pages/view-convertible-instrument.tsx | Instrument detail view with notes |
| NewConvertibleNotePage | pages/new-convertible-note.tsx | Issue a new note against an instrument |
| EditConvertibleNotePage | pages/edit-convertible-note.tsx | Edit note details |
| ConversionPage | pages/conversion-page.tsx | Execute conversion with valuation and share distribution |
Components
| Component | File | Purpose |
|---|---|---|
| ConvertibleInstrumentsForm | components/convertible-instruments-form.tsx | Shared create/edit form for instrument terms |
| ConvertibleNoteForm | components/convertible-note-form.tsx | Note issuance form (principle, owner, date) |
| InstrumentDetails | components/instrument-details.tsx | Read-only display of instrument terms and calculated values |
| ConversionForm | components/conversion-form.tsx | Conversion valuation input + per-note share distribution |
Routes
| Route | Component |
|---|---|
/organization/:org/capitalization/block/:block/convertible | ListConvertibleInstrumentsPage |
/organization/:org/capitalization/block/:block/convertible/new | NewConvertibleInstrumentPage |
/organization/:org/capitalization/block/:block/convertible/view/:holding | ViewConvertibleInstrumentPage |
/organization/:org/capitalization/block/:block/convertible/view/:holding/edit | EditConvertibleInstrumentPage |
/organization/:org/capitalization/block/:block/convertible/view/:holding/note/new | NewConvertibleNotePage |
/organization/:org/capitalization/block/:block/convertible/view/:holding/note/:note/edit | EditConvertibleNotePage |
/organization/:org/capitalization/block/:block/convertible/view/:holding/conversion | ConversionPage |
paths.ts lines 118–126, routes.ts lines 689–715
Form Fields
ConvertibleInstrumentFormFields (17 fields,types.ts lines 3–21):
| Field | Type | Description |
|---|---|---|
| instrumentType | ConvertibleInstrumentType | Selected instrument template |
| name | string | Display name |
| abbreviation | string | Short label |
| authorizedRaiseAmount | number | Max capital |
| interestRate | number | Annual rate |
| accrualFrequency | AccrualFrequency | Compounding period |
| maturityDate | Date | Maturity date |
| valuationMax | number | Valuation cap |
| valuationMin | number | Valuation floor |
| conversionDiscount | number | Discount percentage |
| earlyExitMultiple | number | Early exit return multiple |
| estimatedConversionPrice | number | Estimated share price |
| qualifiedFinancingAmount | number | Auto-conversion threshold |
| pricePerUnit | number | Price per unit |
| seniority | number | Liquidation priority |
| convertsTo | string | Target security type for conversion |
| approvalDocument | string | Authorization document reference |
types.ts lines 23–27):
| Field | Type | Description |
|---|---|---|
| principle | number | Investment amount (USD) |
| owner | string | Note holder (member reference) |
| issueDate | Date | Date the note was issued |
6. Business Rules and Validation
6.1 Interest Calculations
The module provides 17 calculation functions (utility.ts):
| Function | Purpose |
|---|---|
calculateSimpleInterest(steps, principle, rate) | Simple interest: steps * principle * rate |
calculateCompoundInterest(steps, principle, rate) | Compound: principle * (1 + rate)^steps - principle |
getAccrualSteps(start, end, frequency) | Count accrual periods between dates |
getInterestFromConvertible(instrument, issueDate, value, current?, total?) | Full interest calculation using instrument terms |
optionalInterestFromConvertible(...) | Null-safe wrapper for optional instrument data |
getSharePrice(usd, totalUnits, discount) | Price per share: usd / totalUnits * (1 - discount) |
convertToShares(usdAmount, price) | Shares: usdAmount / price |
getMaxShares({maxUsd, valuationMin, totalUnits, discount}) | Maximum shares at conversion |
getOrganizationTotalUnits(securities) | Sum outstanding across all security types |
6.1a Fully Diluted Equation (from Confluence KnowledgeBase)
Source: Confluence KnowledgeBase — Convertible Note Fully Diluted Equation (by Christopher Johnson, extracted from source code)Inputs:
- From Convertible Instrument: Accrual Frequency, Discount Rate, Interest Rate, Maturity Date (optional), Minimum Valuation
- From Convertible Note: Issue Date, Principle
- From Organization: Total Units
- End Date = Maturity Date (if specified), otherwise Today
- If End Date is before Issue Date, Fully Diluted = 0
- Accrual Steps calculation:
- Daily: number of days from Issue Date to End Date (not counting Issue Date)
- Monthly: days rounded down to the most recent day-of-month matching Issue Date
- Annual: days rounded down to the most recent matching day-of-month AND month
Interest = Principle * Accrual Steps * Interest Rate / 365 / 100
6.2 Conversion Rules
- User enters organization valuation at time of conversion
- System calculates shares owed per note: note value (principle + accrued interest) divided by conversion price
- Conversion price = min(valuation cap / total units, valuation / total units) * (1 - discount)
- Per-note share distributions can be manually adjusted before execution
- Conversion creates new Holdings (destination) linked back to source notes via NoteConversions
- The InstrumentConversions record captures the total shares and valuation used
6.3 Validation
| Rule | Enforcement |
|---|---|
| Valuation must be non-zero | Conversion form: nonZeroCurrencyField validator |
| Instrument type is required | Form validation |
| Authorized raise amount is required | Entity constraint (non-nullable numeric) |
| All content-addressed records are immutable | HashedTable base class — updates create new hash entries |
6.4 Instrument Type Behavior
Different instrument types control which form fields are displayed (dynamic fields inutility.ts lines 200–206):
- SAFEs: No interest rate, no maturity date, no accrual frequency
- Convertible notes: Interest rate, accrual frequency, maturity date are active
- Custom: All fields available
7. Acceptance Criteria
- All 9 instrument types can be created with the correct fields active/hidden per type
- Convertible notes accrue interest correctly using simple or compound calculation based on accrual frequency
- Interest calculation matches the formula: simple =
steps * principle * rate, compound =principle * (1 + rate)^steps - principle - Conversion calculates shares owed per note using valuation, discount, and cap
- Conversion creates new destination Holdings and NoteConversion records
- Repayment creates a Repayment record with the instrument reference and date
- Instrument terms are immutable (content-addressed via HashedTable)
- Form validation prevents zero-valuation conversions
- The convertibles list page shows all instruments in a capitalization block
- Note issuance captures principle, owner, and issue date
- Share distribution amounts in the conversion form default to calculated shares owed
-
getMaxSharescorrectly computes maximum shares using valuation floor and discount - All 8 frontend routes resolve to the correct page components
8. Risks and Edge Cases
| Risk | Impact | Mitigation |
|---|---|---|
| Numeric precision loss in share calculations | Incorrect share counts | Use numeric (arbitrary precision) in DB; decimal library in frontend |
| No server-side validation in persistence layer | Invalid instrument data could be stored | Add validation at API request handler level before persistence calls |
| Conversion form allows manual share adjustment without total validation | Output shares may not equal calculated total | Add frontend validation that sum(distributions) equals expected total |
| Zero-interest instruments with accrual frequency set | Unnecessary calculation cycles | Skip interest calculation when interestRate is 0 or null |
| Multiple concurrent conversions on same instrument | Double-converted shares | Serialize conversion requests per instrument; check conversion status before executing |
| SAFE vs note field confusion | User enters interest rate on a SAFE | Dynamic field visibility per instrument type already handles this |
| Valuation cap below valuation floor | Contradictory terms | Validate valuationMax >= valuationMin in form |
9. Dependencies
| Dependency | Type | Status |
|---|---|---|
| 003-Cap Table (Holdings, SecurityTypes) | Required before | DRAFT |
| 012-Team Members and Roles (member references) | Required before | DRAFT |
| 002-Organization Management | Required before | DRAFT |