Skip to content

Add makePermissionDecoderConfigs to resolve the PermissionDecoderConfigs to be used with @metamask/gator-permissions-controller#259

Merged
jeffsmale90 merged 16 commits into
mainfrom
feat/permission-decoders
Jun 18, 2026
Merged

Add makePermissionDecoderConfigs to resolve the PermissionDecoderConfigs to be used with @metamask/gator-permissions-controller#259
jeffsmale90 merged 16 commits into
mainfrom
feat/permission-decoders

Conversation

@jeffsmale90

@jeffsmale90 jeffsmale90 commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

📝 Description

This PR relocates the PermissionDecoderConfigs from @metamask/gator-permissions-controller into @metamask/7715-permission-types.

🔄 What Changed?

Adds the following to @metamask/7715-permission-types:

  • makePermissionDecoderConfigs function to resolve the PermissionDecodeConfigs
  • ExpiryRule, PayeeRule, and RedeemerRule types
  • Permission decoders now use caveat terms decoders from @metamask/delegation-core instead of implementing this logic themselves

Note: erc20-token-allowance and native-token-allowance still have bespoke implementations as they use periodic caveats with UINT256_MAX as the period duration, which is decoded as a number. These will be replaced with basic allowance caveats, at which point these bespoke decodes can be discarded.

🚀 Why?

Presently in order to add a new permission type, changes must be made to @metamask/smart-accounts-kit, @metamask/7715-permission-types, @metamask/gator-permissions-snap, @metamask/gator-permissions-controller.

By moving all of the logic to define the permission types into @metamask/7715-permission-types, and importing these into the various packages that need these definitions, the bulk of the changes will be made in this one place.

🧪 How to Test?

Describe how to test these changes:

⚠️ Breaking Changes

List any breaking changes:

  • No breaking changes
  • Breaking changes (describe below):

📋 Checklist

Check off completed items:

  • Code follows the project's coding standards
  • Self-review completed
  • Documentation updated (if needed)
  • Tests added/updated
  • Changelog updated (if needed)
  • All CI checks pass

🔗 Related Issues

Link to related issues:
Closes #
Related to #

📚 Additional Notes

Any additional information, concerns, or context:


Note

Medium Risk
Relocates delegation caveat decoding and validation used to interpret execution permissions; incorrect decoding could misread limits, payees, or redeemers, though behavior is heavily tested and mostly delegated to delegation-core.

Overview
Moves permission decoder configuration from @metamask/gator-permissions-controller into @metamask/7715-permission-types, so consumers can build chain-specific decoders from one shared package.

makePermissionDecoderConfigs takes deployed delegation contracts, resolves checksummed enforcer addresses, and returns seven PermissionDecoderConfig entries (native/ERC-20 stream, periodic, allowance, plus token-approval-revocation). The package now exports ExpiryRule, PayeeRule, and RedeemerRule alongside that factory.

Each config wires required/optional enforcers, rule decoders (expiry, redeemer, ERC-20 or native payee), and validateAndDecodeData that turns on-chain caveats into typed permission payloads. Most types use @metamask/delegation-core term decoders; erc20/native-token-allowance still use custom parsing for periodic enforcer terms with UINT256_MAX period duration.

Adds runtime dependencies on delegation-core, delegation-deployments, and utils, Vitest coverage for decoders and rules, and test-focused ESLint/tsconfig updates.

Reviewed by Cursor Bugbot for commit 1930bd9. Bugbot is set up for automated code reviews on this repo. Configure here.

@socket-security

socket-security Bot commented Jun 3, 2026

Copy link
Copy Markdown

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@jeffsmale90 jeffsmale90 force-pushed the feat/permission-decoders branch 8 times, most recently from 92f2173 to cbda271 Compare June 9, 2026 02:31
@jeffsmale90 jeffsmale90 force-pushed the feat/permission-decoders branch from cbda271 to e37bee7 Compare June 9, 2026 02:37
@jeffsmale90 jeffsmale90 changed the title First pass at permission decoders Add makePermissionDecoderConfigs to resolve the PermissionDecoderConfigs to be used with @metamask/gator-permissions-controller Jun 9, 2026
@jeffsmale90 jeffsmale90 marked this pull request as ready for review June 9, 2026 03:17
@jeffsmale90 jeffsmale90 requested a review from a team as a code owner June 9, 2026 03:17
Comment thread packages/7715-permission-types/src/permissions/caveats/erc20TokenAllowance.ts Outdated
Comment thread packages/7715-permission-types/src/permissions/caveats/erc20TokenAllowance.ts Outdated
@jeffsmale90 jeffsmale90 force-pushed the feat/permission-decoders branch from 7b75406 to 1930bd9 Compare June 9, 2026 23:17

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1930bd9. Configure here.

mj-kiwi
mj-kiwi previously approved these changes Jun 11, 2026

@mj-kiwi mj-kiwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, tiny nits to fix.

periodDuration?: number;
startDate?: number;
} = {}): Hex =>
createERC20TokenPeriodTransferTerms(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

makeTerms currently uses createERC20TokenPeriodTransferTerms, which validates inputs before producing the encoded terms. That means the zero periodAmount / zero periodDuration tests are exercising delegation-core validation rather than this decoder's validation branches.

Can we construct the raw hex terms manually here, like nativeTokenPeriodic.test.ts does, so the test actually reaches the decoder checks in this package?

@jeffsmale90 jeffsmale90 Jun 14, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good call - I've updated this.


if (matchingCaveats.length === 0) {
if (throwIfNotFound ?? true) {
throw new Error('Invalid caveats');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The missing/duplicate caveat error from getTermsByEnforcer is currently just Invalid caveats, which makes debugging pretty hard. Could we include the enforcer address and distinguish missing vs duplicate matches?

Something like:
Invalid caveats: missing terms for enforcer ${enforcer}
Invalid caveats: multiple caveats found for enforcer ${enforcer}

@jeffsmale90 jeffsmale90 Jun 14, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've refactored this function to improve readability, and updated the error messages as you suggested.

import type { ChecksumCaveat } from '../../../src/permissions/types';
import { getChecksumEnforcersByChainId } from '../../../src/permissions/utils';

describe('expiryRule', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The expiry rule has several explicit error paths that are not covered by tests yet:

  • invalid terms length
  • timestampBeforeThreshold <= 0
  • timestampAfterThreshold !== 0

Can we add tests for those branches so the validation behavior is locked down?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added tests to cover these cases

}

const [caveat] = matchingCaveats;
if (!caveat) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This if (!caveat) branch appears unreachable: the code already returned when there were zero matches and already throws when there is more than one match. Also, the error message here says “multiple” even though this branch would imply no caveat.

Can we remove this dead branch?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's actually reimplementing the functionality in getTermsByEnforcer, so I've updated it to use that utility.

@jeffsmale90 jeffsmale90 requested a review from mj-kiwi June 14, 2026 22:31

@mj-kiwi mj-kiwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some nits may need to be addressed before merging.

"vitest": "^3.2.4"
},
"dependencies": {
"@metamask/delegation-core": "^2.2.1",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move @metamask/delegation-deployments to devDependencies. I think it’s only used for tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

👍

I think we could potentially remove the dependency entirely - we don't strictly need the dependency, as we could make up environment values, but it's easier to import 🤷

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

makePermissionDecoderConfigs returns PermissionDecoderConfig[] and accepts DeployedContractsByName, but neither type is exported from src/index.ts. Consumers who want to annotate a variable or write a helper that accepts/returns these types have no way to reference them. Please export both.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good call! These are the types that I mentioned this morning :)

Updated!

throw new Error('Invalid native-token-allowance terms: expected 96 bytes');
}

const [allowanceAmount, periodDurationRaw, startTimeRaw] = splitHex(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

splitHex always prepends 0x to every segment, so every returned value is truthy — the if (!tokenAddress || !allowanceAmount || ...) guard can never fire. The byte-length check above it is the real defence. Please remove this dead guard

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

splitHex returns Hex[] of indeterminate length, meaning each of the destructured values is Hex | undefined.

i've updated splitHex to return a Hex[] of the same length as the specified lengths array, meaning we can remove the guard as you suggest 👍


const [tokenAddress, allowanceAmount, periodDurationRaw, startTimeRaw] =
splitHex(terms, [20, 32, 32, 32]);
if (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

splitHex always prepends 0x to every segment, so every returned value is truthy — the if (!tokenAddress || !allowanceAmount || ...) guard can never fire. The byte-length check above it is the real defence. Please remove this dead guard

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

as above - fixed with an update to splitHex 👍

@jeffsmale90 jeffsmale90 requested a review from mj-kiwi June 18, 2026 03:18

@mj-kiwi mj-kiwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@jeffsmale90 jeffsmale90 merged commit f059043 into main Jun 18, 2026
17 checks passed
@jeffsmale90 jeffsmale90 deleted the feat/permission-decoders branch June 18, 2026 20:46
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