Skip to content

spec: Health Triger Manager#12

Open
janezpodhostnik wants to merge 3 commits intomainfrom
janez/HTM
Open

spec: Health Triger Manager#12
janezpodhostnik wants to merge 3 commits intomainfrom
janez/HTM

Conversation

@janezpodhostnik
Copy link
Copy Markdown
Contributor

No description provided.

Comment thread health-trigger-manager.md Outdated
Comment thread docs/health-trigger-manager.md
Comment thread health-trigger-manager.md Outdated
Comment thread health-trigger-manager.md Outdated
Comment thread health-trigger-manager.md Outdated
Comment thread health-trigger-manager.md Outdated
Comment thread health-trigger-manager.md Outdated
Comment thread health-trigger-manager.md Outdated
@janezpodhostnik janezpodhostnik force-pushed the janez/HTM branch 6 times, most recently from 2b48af5 to 3580e0e Compare April 23, 2026 15:02
Comment thread docs/health-trigger-manager.md
Comment thread docs/health-trigger-manager.md Outdated
Comment thread docs/health-trigger-manager.md Outdated

### Registration

To register a health trigger the caller provides a position ID, a health function $$H(P)$$, and the three fields of a `HealthTrigger` (bounds, callback, provider). If the position already has an entry in the registry, the health function is ignored and the new trigger is appended to the existing entry's trigger list.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems dangerous, a malicious actor could register a position with a health function that always returns 1.5, essentially bricking all other callbacks on that position.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have a solution for this, basically requiring the provider to be a provider: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault> do you still see a way it could panic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

never mind actually. this could still panic! I will change this altogether

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed in 30cce52

Comment thread docs/health-trigger-manager.md Outdated
Comment thread docs/health-trigger-manager.md Outdated
**Validation:** The **HTM** evaluates $$H(P)$$ at registration time. Registration is rejected if:

- $$H(P)$$ returns `nil` (the position does not exist or its health cannot be determined).
- The position is already out of bounds ($$H(P) < H_{\textbf{min}}$$ or $$H(P) > H_{\textbf{max}}$$, for whichever bounds are specified). This prevents registering triggers that would fire immediately, which would be wasteful and could mask bugs in the caller's logic.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If possible the registration function should not panic, or we would have to wrap any registration from the FYV into a scheduled transaction.
If the health function never panics this is fine.

However in almost all cases this should have been already checked by the calling function and if health has not been checked by the calling function, then it would have to check if after registration.

Is there any reason we shouldn't accept registrations which trigger immediatly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can make registration more lax. I just saw no point registering something that will trigger immediately, but maybe it makes the rest of the code easier.

I will see if we can add a guarantee to the Register function that it will not panic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

made registration more lax and added guarantee it wont panic, but I had to add a limitation that you cannot have high priority scheduling. c0f5578

Comment thread docs/health-trigger-manager.md
Comment thread docs/health-trigger-manager.md Outdated
Comment thread docs/health-trigger-manager.md Outdated
- If $$H_{\textbf{min}} \leq H(P) \leq H_{\textbf{max}}$$ (within bounds) → no action. Continue to the next trigger.
- The trigger is out of bounds. Validate capabilities:
- The `HealthCallback` capability still exists.
- The `Provider` capability still exists and has sufficient funds to pay for the callback TX.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Provider is an interface and may panic, disrupting everything indefinitely.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

made provider more specific so that it has to have a flow vault underneath.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed in 30cce52

Comment thread docs/health-trigger-manager.md Outdated

**Return value:** Registration returns success or failure. Failure indicates one of the validation checks above was not met.

**Access control:** Registration is public. Since the registering party pays for callback execution (via the `Provider` capability), there is no execution cost to the protocol and no need for additional permissioning. Storage costs are absorbed by the **HTM** account for now (see [Future Expansions](#7-future-expansions)).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like to assume that its public for any of the other security considerations to protect against our own misuse.
But I would restrict it at the beginning.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Looking at this again, registration might need to be guarded, but process doesn't need to be... I will check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

registration is now gated and does not accept a provider: 30cce52

@janezpodhostnik janezpodhostnik marked this pull request as ready for review April 23, 2026 18:37
@janezpodhostnik janezpodhostnik requested a review from a team as a code owner April 23, 2026 18:37
Comment thread docs/health-trigger-manager.md
Comment thread docs/health-trigger-manager.md

The registry is keyed by position ID ($$P$$ = `position.id`, i.e. the Position resource's UUID). Each entry contains a list of `HealthTrigger` objects for that position.

Position health is obtained via `FlowALP.Pool.getPositionHealth(positionID)`, which returns `UFix64?`. A `nil` return means the health could not be determined (see [Process](#process) for how this case is handled). The `Registry` holds a single `Capability<&FlowALP.Pool>`, set at init and changeable by admin. This ensures [Process](#process) calls the health function only once per position, regardless of how many triggers are registered for it.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

2 thoughts, expanding the Q&A session today:

  • I think for the immediate term it's fine to just say getPositionHealth will panic if we can't get the health for any reason. We don't expect that to happen, but if it does we'll manually restart the HTM.
  • When we need to deal with recoverable error conditions, I agree with Alex that we should explicitly encode those in the return value of the fail-able function.

Copy link
Copy Markdown
Contributor Author

@janezpodhostnik janezpodhostnik Apr 24, 2026

Choose a reason for hiding this comment

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

So you are suggesting to just panic in case of nil for now and we can later add the disambiguation?

If that is the case, wouldn't it be simpler/cleaner to just have health return an UFix instead of UFix? for now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm just saying we have the freedom to panic, now, in the implementation, if that simplifies your life.

The spec should be more forward-looking. I think the spec should, when describing how we deal with multiple distinct failure conditions, use Alex's suggestion of enumerating the possible failure modes in the return type. Since we haven't decided on a structure for doing that, I would just say:

  • getPositionHealth returns either a health value, or an error.
  • The error structure is TBD, and should be a common pattern across the project, but would be similar to [Either types](https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Either.html
  • Before this error handling structure is defined, implementations may panic on failure. (if this saves time, or we can just define and implement the structure)

hMin: UFix64?,
hMax: UFix64?,
callbackAddress: Address,
reason: String
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
reason: String
reason: TriggerRemovalReason

Maybe define an enum type for reason? Although I guess Cadence probably just puts the raw enum integer value into the event data -- it would be nice to still have the string representation in the actual event.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure about this one! There is noone on chain that will be looking for this info and the offchain processes wont care that this is an enum. Enums also have issues with contract upgrades... so if not really necessary I would say lets just have strings.

Comment thread docs/health-trigger-manager.md
Comment thread docs/health-trigger-manager.md
Copy link
Copy Markdown
Member

@jordanschalm jordanschalm left a comment

Choose a reason for hiding this comment

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

👏 Great work

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.

3 participants