predict: add App-witness gated custom-owner PredictManager constructor#1010
Open
SeventhOdyssey71 wants to merge 1 commit into
Open
predict: add App-witness gated custom-owner PredictManager constructor#1010SeventhOdyssey71 wants to merge 1 commit into
SeventhOdyssey71 wants to merge 1 commit into
Conversation
Mirrors the patterns already in deepbook::registry + deepbook::balance_manager. Lets external custodian protocols (margin loans, structured vaults, range ladders, PLP+hedge) mint a PredictManager whose owner is a contract-derived address rather than the transaction sender. predict_manager.move - new() is now a thin wrapper around new_internal that hardcodes ctx.sender() as the owner — behaviour-equivalent to before. - new_with_custom_owner(registry_uid, owner, ctx) is the new package-internal constructor. Wraps the inner BalanceManager via balance_manager::new_with_custom_owner(owner, ctx), so when `owner` is a contract-derived address the BalanceManager is effectively locked to that contract. registry.move - AppKey<phantom App: drop> dynamic field marker. - authorize_app<App>(registry, admin_cap) — admin-gated. - deauthorize_app<App>(registry, admin_cap): bool. - assert_app_is_authorized<App>(registry). - create_manager_for_custodian<App>(registry, owner, ctx) public entry. Double-gated: admin-set AppKey authorization + type-system witness. Calls predict_manager::new_with_custom_owner. tests/registry/custodian_tests.move - happy path: manager owner is the custodian address, not the sender. - unauthorized App aborts with EAppNotAuthorized. - deauthorize_app removes access, returns true. - owner == sender path still works (custom-owner is general). No changes to existing call sites. Existing test suite unaffected. Use case unblocked: composable custodian protocols on top of Predict. Today there is no way for an external contract to escrow a Predict position — the manager's owner is always ctx.sender(), and the caps are sealed inside. DeepBook Margin solves this exact problem for BalanceManager via new_with_custom_owner_caps<App>; this PR brings the same pattern to PredictManager. See .PR-BODY.md (not committed) for full PR description with motivation + test plan + security analysis.
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
predict: add App-witness gated custom-owner PredictManager constructor
Summary
Adds a small App-witness authorization layer to
predict::registryand anew_with_custom_ownerconstructor topredict::predict_manager, mirroringthe patterns already in
deepbook::registryanddeepbook::balance_manager. Net new public surface:No changes to any existing call site. The current
predict_manager::newbecomes a thin wrapper that calls a shared
new_internalwithctx.sender()as the owner — behaviour-equivalent to today.Motivation
Right now, the only way to create a
PredictManageris viaregistry::create_manager(...), which hardcodes the owner toctx.sender(). That works for end users, but it makes it impossible tobuild composable protocols that need to escrow a Predict position
inside a contract-controlled custodian.
Concrete use cases this unblocks:
Margin / lending against options. A vault that lends USDC against
an open Predict position needs to be able to mint the position into a
manager only the vault controls. Today the manager's owner is always
the borrower, so the borrower can withdraw the collateral out from
under the loan.
Vol Yield / structured products. A short-vol vault that sells
straddles on Predict and routes the strategy programmatically needs a
contract-owned manager, not a user-owned one.
Range-ladder / PLP-hedge vaults. Same shape — the vault is the
position owner.
The
deepbookside of the monorepo already solves this exact problemfor
BalanceManagervianew_with_custom_owner+new_with_custom_owner_caps<App>, with admin-gatedAppwitnessauthorization on the
Registry. This PR brings the same pattern topredict_manager, which is the analogous custody object for the Predictprotocol.
Why this is the minimal change
Every new line copies a precedent that already exists in
packages/deepbook:packages/deepbook)AppKey<phantom App: drop>deepbook::registry::AppKeyauthorize_app<App: drop>deepbook::registry::authorize_appdeauthorize_app<App: drop>deepbook::registry::deauthorize_appassert_app_is_authorized<App: drop>deepbook::registry::assert_app_is_authorizednew_with_custom_owner(registry_uid, owner, ctx)balance_manager::new_with_custom_owner(owner, ctx)create_manager_for_custodian<App>entrybalance_manager::new_with_custom_owner_caps<App>Reviewers can verify by diffing against the corresponding deepbook code
side-by-side.
Security
The new entry point is double-gated:
AdminCapcan callauthorize_app<App>(...). So an unprivileged caller cannot grantitself custodian access.
Appis a phantom witness type from thecustodian's own package. The Move type system enforces that only the
package that defined
Appcan reference it as the type parameter tocreate_manager_for_custodian<App>. So even if two distinctprotocols both authorize a witness called
MyApp, they are differenttypes and the auth is scoped per type.
The created manager's owner is whatever address the custodian passes —
typically the manager's own derived address, or the address of the
custodian's outer shared object. Same shape DeepBook Margin already uses
to lock its own BalanceManager.
Test plan
packages/predict/tests/registry/custodian_tests.move:authorize_then_create_for_custodian_succeeds— happy path; managerowner is the custodian address, not the tx sender.
unauthorized_app_aborts—create_manager_for_custodian<UnauthorizedApp>aborts with
EAppNotAuthorized.deauthorize_removes_access—deauthorize_appreturnstrueandthe App key is removed.
create_for_custodian_with_owner_eq_sender_still_works— thecustom-owner path is general;
owner == senderworks the same ascreate_manager.new()signature and behaviourunchanged.
Out of scope
predict::mint,predict::redeem, or related entrypoints. Existing flows continue to work as today.
PredictManagerKeystillhardcodes the secondary index to
0. If/when multi-manager-per-ownerbecomes a requirement, that's a separate, additive change.
How to verify locally
Downstream
Once this lands on
mainand rolls into the next testnet/mainnetdeployment, the following projects can ship:
predict_backed_margin— on-chain margin loans against optionsvol_yield— variance risk premium tokenizationhackathon idea bank