Skip to content

feat(contracts): MCMS-governed LinkTokenAdmin for registry-issued LINK - #1113

Open
JohnChangUK wants to merge 2 commits into
mainfrom
feat/link-token-admin-pkg
Open

JohnChangUK wants to merge 2 commits into
mainfrom
feat/link-token-admin-pkg

Conversation

@JohnChangUK

Copy link
Copy Markdown
Collaborator

Moves privileged LINK operations (mint, burn, transfer, allocate) behind MCMS governance as a standalone dev-only link-token-admin 1.0.0 package. link stays at 2.1.0 untouched, so vetting this does not keep the custom Link.Token templates alive (the NONEVM-5947 migration goal) and no factory re-release is needed on link bumps.

Design

MCMS exercises MCMSReceiver_Entrypoint on LinkTokenAdmin to create an authorization fixing the economics (recipient, amount, instrument, validity window). The operator then exercises Execute on the authorization, supplying only plumbing (factory CID, input holding CIDs, registry choice context, disclosures). It cannot alter what was approved.

Execute fetches the live admin contract and re-checks ccipOwner, instanceId and instrumentId, and rejects when the admin is paused. SetPaused and SetObservers archive and recreate the admin, so a stale CID fails the fetch and the operator must supply the current one. Pause is therefore a stop on outstanding authorizations, not just new approvals.

link appears in the test package only, as the factory and holding double.

MCMS wire fix for Decimal params

amount and maxAmount params fields are now encoded with MCMS encodeDecimal (sign byte plus 10^10-shifted magnitude) via the DecimalFields codegen hint. Plain text encoding failed the Daml decode with E_INVALID_PARAMS. The hint is name-keyed, so a few regenerated bindings gain inert hex:"decimal" tags on their own Decimal amount fields. No existing consumer hex-encodes those structs.

Tests

  • Daml: 14 ported scripts plus 4 new pause-enforcement scripts (paused execute, stale CID, foreign admin, unpause). 18 total, all green.
  • Go integration test (integration-tests/mcms/link_token_admin_registry_test.go) against a real DA Registry bootstrap on a CTF network:
    • MCMS bypasser dispatch of ApproveMint, ApproveBurn, SetPaused, SetObservers.
    • ExecuteMint one-step BurnMintFactory_BurnMint on the registry AllocationFactory, both to the registrar itself and to a third-party recipient (the extraActors path). The created registry Holding's owner and amount are asserted in both cases.
    • ExecuteBurn round-trips both holdings.
    • Pause enforcement: stale admin CID fails at fetch (CONTRACT_NOT_FOUND), paused admin fails the pause assert, unpausing restores execution.
  • Not exercised against the real registry: Transfer and Allocate (covered against the LinkRegistry and stub-factory doubles in the Daml tests only).

Release

Dev-only in this PR. The link-token-admin 1.0.0 released DAR follows in a dedicated release PR.

Follow-ups (deployment changesets, operator Execute tooling, EDS kill switch, forged-holdings check) are tracked as NONEVM-5947 follow-ups.

…TokenAdmin

Standalone dev-only package (link stays 2.1.0, untouched) holding the
MCMS-governed admin surface for registry-issued LINK: Approve/Execute
mint, burn, transfer and allocate, plus SetPaused/SetObservers.

Execute* takes the live admin contract id and re-checks it: the fetched
admin must match the authorization's ccipOwner, adminInstanceId and
instrumentId, and must not be paused. SetPaused/SetObservers rotate the
admin cid, so a stale cid fails the fetch and the operator must supply
the current one. Pause stops outstanding authorizations, not just new
approvals.

link appears in the test package only, as the factory/holding double.
Daml tests cover the entrypoint paths, failure paths and pause
enforcement (18 scripts).

MCMS wire fix for Decimal params: amount and maxAmount fields are
encoded with MCMS encodeDecimal (sign byte plus 10^10-shifted
magnitude) via the DecimalFields codegen hint. The hint is name-keyed,
so regenerated bindings for other packages gain inert hex:"decimal"
tags on their own Decimal amount fields. No existing consumer
hex-encodes those structs.
…egistry

Integration test driving the governed lifecycle on a one-party CTF
network: MCMS bypasser dispatch of ApproveMint, ApproveBurn, SetPaused
and SetObservers; ExecuteMint one-step BurnMintFactory_BurnMint on the
bootstrap registry AllocationFactory, both to the registrar itself and
to a third-party recipient (extraActors path), asserting the resulting
registry Holding's owner and amount; ExecuteBurn round-trips both
holdings; and the Execute* pause enforcement: a stale admin cid fails
at fetch, a paused admin fails the pause assert, unpausing restores
execution.
@JohnChangUK
JohnChangUK requested a review from a team as a code owner October 7, 2026 15:26
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

👋 JohnChangUK, thanks for creating this pull request!

To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team.

Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks!

This branch has not been deployed

No deployments
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.

1 participant