diff --git a/Test/daml/OpenCapTable/TestWarrantConversionRightReferences.daml b/Test/daml/OpenCapTable/TestWarrantConversionRightReferences.daml new file mode 100644 index 00000000..c8e7dd6c --- /dev/null +++ b/Test/daml/OpenCapTable/TestWarrantConversionRightReferences.daml @@ -0,0 +1,103 @@ +module OpenCapTable.TestWarrantConversionRightReferences where + +import Fairmint.OpenCapTable.OCF.WarrantIssuance +import Fairmint.OpenCapTable.Types.Conversion +import Fairmint.OpenCapTable.Types.Monetary +import qualified Fairmint.OpenCapTable.CapTable as CT +import OpenCapTable.Setup +import OpenCapTable.TestHelpers +import Daml.Script + +warrantConversionTriggerTo : Optional Text -> OcfConversionTrigger +warrantConversionTriggerTo stockClassId = OcfConversionTrigger with + conversion_right = OcfRightWarrant (OcfWarrantConversionRight with + conversion_mechanism = OcfWarrantMechanismFixedAmount with converts_to_quantity = 100.0 + converts_to_future_round = None + converts_to_stock_class_id = stockClassId + type_ = "WARRANT_CONVERSION_RIGHT") + end_date = None + nickname = None + start_date = None + trigger_condition = None + trigger_date = None + trigger_description = None + trigger_id = "TRIGGER-1" + type_ = OcfTriggerTypeTypeElectiveAtWill + +warrantIssuanceWithConversionTarget : Text -> Text -> Optional Text -> WarrantIssuanceOcfData +warrantIssuanceWithConversionTarget issuanceId securityId stockClassId = WarrantIssuanceOcfData with + id = issuanceId + date = defaultTestDate + security_id = securityId + custom_id = "W-" <> issuanceId + stakeholder_id = defaultStakeholderId + board_approval_date = None + stockholder_approval_date = None + consideration_text = None + security_law_exemptions = [] + quantity = Some 100.0 + quantity_source = None + exercise_price = Some (OcfMonetary with amount = 1.0, currency = "USD") + purchase_price = OcfMonetary with amount = 0.0, currency = "USD" + exercise_triggers = [warrantConversionTriggerTo stockClassId] + warrant_expiration_date = None + vesting_terms_id = None + vestings = [] + comments = [] + +testWarrantConversionRightTargetCreatedInSameBatchSucceeds = script do + TestOcp{issuer, cap_table} <- setupTestOcp + capTableWithStakeholder <- addDefaultStakeholder issuer cap_table + + _ <- submit issuer do + exerciseCmd capTableWithStakeholder CT.UpdateCapTable with + creates = + [ CT.OcfCreateStockClass (defaultStockClass "SC_WARRANT_TARGET") + , CT.OcfCreateWarrantIssuance (warrantIssuanceWithConversionTarget "TX_WARRANT_TARGET_REF" "WARRANT-TARGET-REF" (Some "SC_WARRANT_TARGET")) + ] + edits = [] + deletes = [] + pure () + +testWarrantConversionRightMissingTargetFails = script do + TestOcp{issuer, cap_table} <- setupTestOcp + capTableWithStakeholder <- addDefaultStakeholder issuer cap_table + + submitMustFail issuer do + exerciseCmd capTableWithStakeholder CT.UpdateCapTable with + creates = [CT.OcfCreateWarrantIssuance (warrantIssuanceWithConversionTarget "TX_WARRANT_MISSING_TARGET" "WARRANT-MISSING-TARGET" (Some "SC_MISSING_WARRANT_TARGET"))] + edits = [] + deletes = [] + +testWarrantConversionRightEditMissingTargetFails = script do + TestOcp{issuer, cap_table} <- setupTestOcp + capTableWithStakeholder <- addDefaultStakeholder issuer cap_table + result <- submit issuer do + exerciseCmd capTableWithStakeholder CT.UpdateCapTable with + creates = [CT.OcfCreateWarrantIssuance (warrantIssuanceWithConversionTarget "TX_WARRANT_EDIT_TARGET" "WARRANT-EDIT-TARGET" None)] + edits = [] + deletes = [] + + submitMustFail issuer do + exerciseCmd result.updatedCapTableCid CT.UpdateCapTable with + creates = [] + edits = [CT.OcfEditWarrantIssuance (warrantIssuanceWithConversionTarget "TX_WARRANT_EDIT_TARGET" "WARRANT-EDIT-TARGET" (Some "SC_MISSING_WARRANT_EDIT_TARGET"))] + deletes = [] + +testWarrantConversionRightTargetDeleteCannotLeaveDanglingReference = script do + TestOcp{issuer, cap_table} <- setupTestOcp + capTableWithStakeholder <- addDefaultStakeholder issuer cap_table + result <- submit issuer do + exerciseCmd capTableWithStakeholder CT.UpdateCapTable with + creates = + [ CT.OcfCreateStockClass (defaultStockClass "SC_WARRANT_DELETE_TARGET") + , CT.OcfCreateWarrantIssuance (warrantIssuanceWithConversionTarget "TX_WARRANT_DANGLING_TARGET" "WARRANT-DANGLING-TARGET" (Some "SC_WARRANT_DELETE_TARGET")) + ] + edits = [] + deletes = [] + + submitMustFail issuer do + exerciseCmd result.updatedCapTableCid CT.UpdateCapTable with + creates = [] + edits = [] + deletes = [CT.OcfDeleteStockClass "SC_WARRANT_DELETE_TARGET"] diff --git a/dars/OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar b/dars/OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar index a8f7cd61..55a5a349 100644 --- a/dars/OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar +++ b/dars/OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19797ba6b7f7cee39bb9e9c335440ec0e18792a7181d4bdf77d36a91dea5e08e -size 2500577 +oid sha256:3659e1d3773ec6768ed3521a627d4e3b06eb45de34767ef878cf19848a3d73ac +size 2502327 diff --git a/dars/dars.lock b/dars/dars.lock index d416590f..50d425ab 100644 --- a/dars/dars.lock +++ b/dars/dars.lock @@ -12,8 +12,8 @@ ] }, "OpenCapTable-v34/0.0.2/OpenCapTable-v34.dar": { - "sha256": "19797ba6b7f7cee39bb9e9c335440ec0e18792a7181d4bdf77d36a91dea5e08e", - "size": 2500577, + "sha256": "3659e1d3773ec6768ed3521a627d4e3b06eb45de34767ef878cf19848a3d73ac", + "size": 2502327, "sdkVersion": "3.4.10", "uploadedAt": "2026-07-07T21:50:59.634Z", "networks": [] diff --git a/scripts/codegen/templates/CapTable.daml.template b/scripts/codegen/templates/CapTable.daml.template index f31b16c6..acbe8b22 100644 --- a/scripts/codegen/templates/CapTable.daml.template +++ b/scripts/codegen/templates/CapTable.daml.template @@ -18,6 +18,7 @@ import DA.Foldable (mapA_) import DA.Optional (isNone, isSome) import Fairmint.OpenCapTable.Types.Core (Context) +import Fairmint.OpenCapTable.Types.Conversion (OcfAnyConversionRight(..)) import Fairmint.OpenCapTable.Types.Stock (OcfInitialSharesAuthorized(..)) import Fairmint.OpenCapTable.OCF.Issuer (Issuer(..), IssuerOcfData) import Fairmint.OpenCapTable.OCF.Document (Document(..), DocumentOcfData, OcfObjectReference(..), OcfObjectType(..)) @@ -322,6 +323,19 @@ validateConvertibleConversionRightReferences maps = issuance.issuance_data.conversion_triggers) (Map.values maps.convertible_issuances) +validateWarrantConversionRightReferences : CapTableMaps -> Update () +validateWarrantConversionRightReferences maps = + mapA_ + (\issuanceCid -> do + issuance <- fetch issuanceCid + mapA_ + (\trigger -> case trigger.conversion_right of + OcfRightWarrant warrantRight -> + validateOptionalStockClassReference "Warrant conversion target" maps warrantRight.converts_to_stock_class_id + _ -> pure ()) + issuance.issuance_data.exercise_triggers) + (Map.values maps.warrant_issuances) + -- | Get the processing tier for a create operation getCreateTier : OcfCreateData -> Int getCreateTier createData = case createData of @@ -458,6 +472,7 @@ template CapTable validateDocumentRelatedObjects finalMaps newIssuerCid validateStockClassConversionRightReferences finalMaps validateConvertibleConversionRightReferences finalMaps + validateWarrantConversionRightReferences finalMaps validateIssuerAuthorizedShares finalMaps newIssuerCid -- Create new CapTable with updated maps and potentially new issuer