Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions Test/daml/OpenCapTable/TestWarrantIssuance.daml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Fairmint.OpenCapTable.Types.Stock (OcfQuantitySourceType(..), OcfSecurity
import Fairmint.OpenCapTable.Types.Vesting (OcfVesting(..))
import qualified Fairmint.OpenCapTable.CapTable as CT
import OpenCapTable.Setup
import OpenCapTable.TestHelpers (addDefaultStakeholder)
import OpenCapTable.TestHelpers (addDefaultStakeholder, defaultVestingTerms)
import qualified DA.Date as DA
import DA.Date (Month(..))
import qualified DA.Time as DT
Expand Down Expand Up @@ -57,25 +57,28 @@ testWarrantIssuance_OptionalsSome_ArraysNonEmpty = script do
capTableCid <- addDefaultStakeholder issuer cap_table
_ <- submit issuer do
exerciseCmd capTableCid CT.UpdateCapTable with
creates = [CT.OcfCreateWarrantIssuance WarrantIssuanceOcfData with
id = "TX_WARRANT_SOME"
date = DT.time (DA.date 2024 Feb 01) 0 0 0
security_id = "WSEC-3"
custom_id = "W-3"
stakeholder_id = "SH-1"
board_approval_date = Some (DT.time (DA.date 2024 Jan 15) 0 0 0)
stockholder_approval_date = Some (DT.time (DA.date 2024 Jan 20) 0 0 0)
consideration_text = Some "Cash"
security_law_exemptions = [ OcfSecurityExemption with description = "Reg D", jurisdiction = "US-DE" ]
quantity = Some 100.0
quantity_source = Some OcfQuantityInstrumentFixed
exercise_price = Some (OcfMonetary with amount = 1.5, currency = "USD")
purchase_price = OcfMonetary with amount = 0.1, currency = "USD"
exercise_triggers = [ ocfTriggerElectiveAtWill ]
warrant_expiration_date = Some (DT.time (DA.date 2025 Jan 01) 0 0 0)
vesting_terms_id = Some "VT_W"
vestings = [ OcfVesting with date = DT.time (DA.date 2024 Mar 01) 0 0 0, amount = 10.0 ]
comments = ["Issued"]]
creates =
[ CT.OcfCreateVestingTerms (defaultVestingTerms "VT_W")
, CT.OcfCreateWarrantIssuance WarrantIssuanceOcfData with
id = "TX_WARRANT_SOME"
date = DT.time (DA.date 2024 Feb 01) 0 0 0
security_id = "WSEC-3"
custom_id = "W-3"
stakeholder_id = "SH-1"
board_approval_date = Some (DT.time (DA.date 2024 Jan 15) 0 0 0)
stockholder_approval_date = Some (DT.time (DA.date 2024 Jan 20) 0 0 0)
consideration_text = Some "Cash"
security_law_exemptions = [ OcfSecurityExemption with description = "Reg D", jurisdiction = "US-DE" ]
quantity = Some 100.0
quantity_source = Some OcfQuantityInstrumentFixed
exercise_price = Some (OcfMonetary with amount = 1.5, currency = "USD")
purchase_price = OcfMonetary with amount = 0.1, currency = "USD"
exercise_triggers = [ ocfTriggerElectiveAtWill ]
warrant_expiration_date = Some (DT.time (DA.date 2025 Jan 01) 0 0 0)
vesting_terms_id = Some "VT_W"
vestings = [ OcfVesting with date = DT.time (DA.date 2024 Mar 01) 0 0 0, amount = 10.0 ]
comments = ["Issued"]
]
edits = []
deletes = []
pure ()
89 changes: 89 additions & 0 deletions Test/daml/OpenCapTable/TestWarrantIssuanceReferences.daml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
module OpenCapTable.TestWarrantIssuanceReferences where

import qualified Fairmint.OpenCapTable.CapTable as CT
import Fairmint.OpenCapTable.OCF.WarrantIssuance (WarrantIssuanceOcfData(..))
import Fairmint.OpenCapTable.Types.Monetary (OcfMonetary(..))
import Fairmint.OpenCapTable.Types.Stock (OcfQuantitySourceType(..))
import OpenCapTable.HelpersTriggers (ocfTriggerElectiveAtWill)
import OpenCapTable.Setup
import OpenCapTable.TestHelpers
import Daml.Script

warrantIssuance : Text -> Text -> Text -> WarrantIssuanceOcfData
warrantIssuance issuanceId securityId stakeholderId = WarrantIssuanceOcfData with
id = issuanceId
date = defaultTestDate
security_id = securityId
custom_id = "W-" <> issuanceId
stakeholder_id = stakeholderId
board_approval_date = None
stockholder_approval_date = None
consideration_text = None
security_law_exemptions = []
quantity = Some 100.0
quantity_source = Some OcfQuantityUnspecified
exercise_price = Some (OcfMonetary with amount = 1.0, currency = "USD")
purchase_price = OcfMonetary with amount = 0.0, currency = "USD"
exercise_triggers = [ocfTriggerElectiveAtWill]
warrant_expiration_date = None
vesting_terms_id = None
vestings = []
comments = []

createReferencedWarrantIssuance :
Party ->
ContractId CT.CapTable ->
Script (ContractId CT.CapTable, WarrantIssuanceOcfData, Text)
createReferencedWarrantIssuance issuer capTableCid = do
(capTableWithStakeholder, stakeholderId) <- setupStakeholderWithId issuer capTableCid "SH_WARRANT_REF" "Warrant Holder"
let vestingTermsId = "VT_WARRANT_REF"
let issuance = (warrantIssuance "TX_WARRANT_REF" "WARRANT-REF" stakeholderId) with
vesting_terms_id = Some vestingTermsId

result <- submit issuer do
exerciseCmd capTableWithStakeholder CT.UpdateCapTable with
creates =
[ CT.OcfCreateVestingTerms (defaultVestingTerms vestingTermsId)
, CT.OcfCreateWarrantIssuance issuance
]
edits = []
deletes = []
pure (result.updatedCapTableCid, issuance, vestingTermsId)

testWarrantIssuance_MissingVestingTermsReferenceFails = script do
TestOcp{issuer, cap_table} <- setupTestOcp
(capTableCid, stakeholderId) <- setupStakeholderWithId issuer cap_table "SH_WARRANT_MISSING_VESTING" "Warrant Holder"
let issuance = (warrantIssuance "TX_WARRANT_MISSING_VESTING" "WARRANT-MISSING-VESTING" stakeholderId) with
vesting_terms_id = Some "VT_MISSING_WARRANT"

submitMustFail issuer do
exerciseCmd capTableCid CT.UpdateCapTable with
creates = [CT.OcfCreateWarrantIssuance issuance]
edits = []
deletes = []

testWarrantIssuance_VestingTermsCreatedInSameBatchSucceeds = script do
TestOcp{issuer, cap_table} <- setupTestOcp
_ <- createReferencedWarrantIssuance issuer cap_table
pure ()

testWarrantIssuance_EditMissingVestingTermsReferenceFails = script do
TestOcp{issuer, cap_table} <- setupTestOcp
(capTableCid, issuance, _) <- createReferencedWarrantIssuance issuer cap_table
let editedIssuance = issuance with vesting_terms_id = Some "VT_MISSING_WARRANT_ON_EDIT"

submitMustFail issuer do
exerciseCmd capTableCid CT.UpdateCapTable with
creates = []
edits = [CT.OcfEditWarrantIssuance editedIssuance]
deletes = []

testWarrantIssuance_DeleteReferencedVestingTermsFails = script do
TestOcp{issuer, cap_table} <- setupTestOcp
(capTableCid, _, vestingTermsId) <- createReferencedWarrantIssuance issuer cap_table

submitMustFail issuer do
exerciseCmd capTableCid CT.UpdateCapTable with
creates = []
edits = []
deletes = [CT.OcfDeleteVestingTerms vestingTermsId]
4 changes: 2 additions & 2 deletions dars/OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar
Git LFS file not shown
4 changes: 2 additions & 2 deletions dars/dars.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
]
},
"OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar": {
"sha256": "5d0222d7116f1a8278cf65cf7d4d9318ac62c7bf5c30f6bf830fe040fe177ba0",
"size": 2509005,
"sha256": "c89b11662c56c40d0ff7e8bfc03e9f4b863e84ad7b5711a338070a30ed84c08d",
"size": 2510816,
Comment on lines +15 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the existing 0.0.2 DAR backup

This changes the recorded bytes for the already-backed-up OpenCapTable-v34/0.0.2 DAR while OpenCapTable-v34/daml.yaml still declares version 0.0.2. The repo's backup flow treats existing lock keys as immutable (backup-dar refuses to overwrite them), so consumers or upgrade checks that refer to the previous 0.0.2 package can no longer reproduce that exact archive from dars/; for this contract change, bump the package patch and add a new backup instead of replacing the existing 0.0.2 entry.

Useful? React with 👍 / 👎.

"sdkVersion": "3.4.10",
"uploadedAt": "2026-07-07T21:50:59.634Z",
"networks": []
Expand Down
9 changes: 2 additions & 7 deletions scripts/codegen/captable-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,9 @@ validations:
'vesting_terms_id?:vesting_terms',
]
EquityCompensationIssuance:
[
stakeholder_id,
'stock_class_id?:stock_classes',
'stock_plan_id?:stock_plans',
'vesting_terms_id?:vesting_terms',
]
[stakeholder_id, 'stock_class_id?:stock_classes', 'stock_plan_id?:stock_plans', 'vesting_terms_id?:vesting_terms']
ConvertibleIssuance: [stakeholder_id]
WarrantIssuance: [stakeholder_id]
WarrantIssuance: [stakeholder_id, 'vesting_terms_id?:vesting_terms']

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow edits to reference same-batch vesting terms

When an existing warrant is edited to set vesting_terms_id to a VestingTerms created in the same UpdateCapTable call, this new validation makes the generated edit branch assert against the maps before creates are applied. Regular edits are folded before creates in scripts/codegen/templates/CapTable.daml.template lines 472-505, so the new post-batch validateWarrantIssuanceReferences would accept the final state but this early edit validation rejects it. Consider relying on the final invariant for this reference or otherwise validating warrant edits after same-batch creates are present.

Useful? React with 👍 / 👎.

# Stakeholder events - validate stakeholder exists
StakeholderRelationshipChangeEvent: [stakeholder_id]
StakeholderStatusChangeEvent: [stakeholder_id]
Expand Down
9 changes: 9 additions & 0 deletions scripts/codegen/templates/CapTable.daml.template
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,14 @@ validateEquityCompensationIssuanceReferences maps =
validateOptionalReference "Equity compensation vesting terms" maps.vesting_terms issuance.issuance_data.vesting_terms_id)
(Map.values maps.equity_compensation_issuances)

validateWarrantIssuanceReferences : CapTableMaps -> Update ()
validateWarrantIssuanceReferences maps =
mapA_
(\issuanceCid -> do
issuance <- fetch issuanceCid
validateOptionalReference "Warrant vesting terms" maps.vesting_terms issuance.issuance_data.vesting_terms_id)
(Map.values maps.warrant_issuances)

-- | Get the processing tier for a create operation
getCreateTier : OcfCreateData -> Int
getCreateTier createData = case createData of
Expand Down Expand Up @@ -502,6 +510,7 @@ template CapTable
validateWarrantConversionRightReferences finalMaps
validateValuationStockClassReferences finalMaps
validateEquityCompensationIssuanceReferences finalMaps
validateWarrantIssuanceReferences finalMaps
validateIssuerAuthorizedShares finalMaps newIssuerCid

-- Create new CapTable with updated maps and potentially new issuer
Expand Down
Loading