Skip to content

Refactor to parse at allocation creation time. - #8

Draft
jonored wants to merge 2 commits into
jg/cip-0105-externally-implemented-governance-locksfrom
lock-types-and-parsing-refactor
Draft

Refactor to parse at allocation creation time.#8
jonored wants to merge 2 commits into
jg/cip-0105-externally-implemented-governance-locksfrom
lock-types-and-parsing-refactor

Conversation

@jonored

@jonored jonored commented Aug 5, 2026

Copy link
Copy Markdown

Needs tests updated, they were reusing a few functions and a few of the metadata key names have changed to match the design doc.

Pull Request Checklist

Cluster Testing

  • If a cluster test is required, comment /cluster_test on this PR to request it, and ping someone with access to the DA-internal system to approve it.
  • If an upgrade test is required, comment /upgrade_test on this PR to request it, and ping someone with access to the DA-internal system to approve it.
  • If a hard-migration test is required (from the latest release), comment /hdm_test on this PR to request it, and ping someone with access to the DA-internal system to approve it.
  • If a logical synchronizer upgrade test is required (from canton-3.5), comment /lsu_test on this PR to request it, and ping someone with access to the DA-internal system to approve it.

PR Guidelines

  • Include any change that might be observable by our partners or affect their deployment in the release notes.
  • Specify fixed issues with Fixes #n, and mention issues worked on using #n
  • Include a screenshot for frontend-related PRs - see README or use your favorite screenshot tool

Merge Guidelines

  • Make the git commit message look sensible when squash-merging on GitHub (most likely: just copy your PR description).

Needs tests updated, they were reusing a few functions and a few of the
metadata key names have changed to match the design doc.

@meiersi-da meiersi-da left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks. This looks like the right direction.


-- | Key used to provide an effective time to a choice execution, to avoid getTime
effectiveAtKey : Text
effectiveAtKey = "effective-at"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Isn't that a metadata key? I'd suggest we scope that to cip-105/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I was feeding it in as a context key, actually - it should be constrained to an appropriately tight bound - did I hear right that the normal one is one day? - so I figured it might as well be part of the context package from the API instead of part of the user-set metadata. I actually checked here first to see whether we already had a conventional key for "now, but with the more loose time bound" and was somewhat surprised not to find one. We can certainly scope it to cip-105, of course.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This value should be chosen by the client not the off-ledger API server. Let's discuss this evening.

endDate : Time
initialAmount : Decimal
-- ^ The initial amount represents the total that was locked initially when thrown into vesting state
substituteControllers : Optional ControllerSets

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

don't we also need withdraw controllers? I suspect we also need them on the AggregatedLock.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Discussed, and agreed; the case where we have non-joint auth from apps is clear motivation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Withdraw on AggregatedLock is controlled by unlockControllers, though. I think the next state is that both are "withdrawControllers".

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

there might a confusion here:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Needed on the SvLock to pass to the created VestedLock - sorry, I didn't fully remember a bit of the context. Agreed.

@@ -1,148 +1,138 @@
module Splice.AggregateLock where

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
module Splice.GovernanceLock where

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reasonable. I'm not sure whether this module persists separately or gets folded into AmuletAllocationV2 and ExternalPartyAmuletRules, but having it segregated like this is somewhat nice. A bit of a pain with a few bits being close to circular.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Let's keep at least a separate module for the types and the core functions implementing the logic. If needed, we can then inline the functions that assemble the logic into the modules you mentioned.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That's certainly reasonable. I'm just mildly annoyed at the newEmptyAllocation arguments and generic 'a' argument where this is still relying on specifically AmuletAllocationV2 rules and behavior.

Comment on lines +28 to +29
unlockControllers : Optional ControllerSets
substituteControllers : Optional ControllerSets

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

consider grouping into a GovernanceLockControllers record jointly with withdraw

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'll put that back; the main fuss was trying to use different terminology for SvLock and VestingLock, but really, they can probably both use "withdraw" as a key.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'd replied without recovering the full context; having a type with unlock, substitute, and withdraw controllers is certainly appropriate for SvLock and not bad for VestingLock.

substituteControllers : Optional ControllerSets
deriving (Eq, Show)

parseGovernanceLock : CanAssert m => Metadata -> m (Optional GovernanceLock)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would have probably organized this using a type-class FromMetadata a, so that each sub-types parser is defined on its own; and reuse is simplified.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Will do; I usually try to balance direct and local code against type-class and prettier, with smaller amounts leaning towards direct. I'll certainly not say no to splitting it into the type-class.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I usually do the same, but found that for this kind of codec case the overhead on naming and testing becomes significantly lower with a kind of local helper typeclass. For example, this AnyValue codec:

https://github.com/canton-network/splice/blob/df76027a6bdc149184e9ec7911a9978b20c5729e/daml/splice-amulet/daml/Splice/Amulet/TokenApiUtils.daml#L256-L289



data GovernanceLock
= GovernanceLock_SVLocked AggregatedLock

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
= GovernanceLock_SVLocked AggregatedLock
= GL_SvLock SvLock
| GL_FeaturedAppLock FeaturedAppLock
| GL_VestingLock VestingLock

| GovernanceLock_VestingLocked VestingLock
deriving (Eq, Show)

data AggregatedLock =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
data AggregatedLock =
data SvLock =

data AggregatedLock =
AggregatedLock with
lockBeneficiary : Text
unlockControllers : Optional ControllerSets

@meiersi-da meiersi-da Aug 5, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
unlockControllers : Optional ControllerSets
unlockControllers : Optional ControllerSpecification
-- ^ If set, specifies non-default controllers for unlocking the SV lock.

Comment on lines +177 to +185
checkControllerSpecification : TM.TextMap Party -> [Party] -> ControllerSpecification -> Update ()
checkControllerSpecification svRightsParties actors set =
require "Controllers must be one of the listed options" $
any actorsMatch set
where
controllerSetFromMetaMap key = fromOptional [[acctParty]] $ controllerSetFromMeta =<< key `TM.lookup` meta.values
actorsMatch : [SvLockController] -> Bool
actorsMatch set
= all (\s -> any (`checkParty` s) actors) set
&& all (\a -> any (a `checkParty`) set) actors

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

consider converting to [[Party]] and then using TokenStandardUtils.checkActors

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'll keep checkControllerSpecification as the name and contract as a bit of cheap future-proofing, but no good reason to keep the more complex match if SvLockController always maps one-to-one to parties at an instant; I don't think we see that changing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants