Description
Add a new family of Advanced Permission (ERC-7715) types that combine a target/selector restriction with a native-token (or ERC-20) value cap. The currently supported permission types — native-token-stream, native-token-periodic, erc20-token-stream, erc20-token-periodic, erc20-token-revocation — only express value caps and timing, with no way to restrict the call target or function selector.
This schema gap blocks any session-execution pattern that must call a specific contract method instead of transferring raw value (gaming, DeFi strategy bots, auto-rebalance agents, scheduled liquidations, vesting claims, on-chain voting agents, etc.). Without target/selector restrictions, a leaked session key can spend the value cap to an arbitrary recipient, which is unacceptable for any production-grade agent or session-key flow.
The companion option — constructing a custom ERC-7710 delegation with AllowedTargets + AllowedMethods + streaming caveats — is intentionally blocked at the MetaMask Extension signing layer (External signature requests cannot sign delegations for internal accounts.), so the only path forward for general dapps is via Advanced Permissions. That makes filling this schema gap necessary, not optional, for the use cases above.
Proposed new permission types:
// Native token, streaming
{
type: "native-token-function-call-stream",
data: {
target: Address,
selectors: Hex[], // 4-byte function selectors
amountPerSecond: bigint,
initialAmount?: bigint,
maxAmount?: bigint,
startTime?: number,
justification?: string
}
}
// Native token, periodic
{
type: "native-token-function-call-periodic",
data: {
target: Address,
selectors: Hex[],
periodAmount: bigint,
periodDuration: number,
startTime?: number,
justification?: string
}
}
ERC-20 equivalents (erc20-token-function-call-stream, erc20-token-function-call-periodic) for token-paid contract calls in the same shape.
Technical Details
Onchain primitives — already shipped, no new contract work needed.
The Delegation Framework already includes the audited caveat enforcers required to express this permission shape:
- AllowedTargetsEnforcer — restricts the call target.
- AllowedMethodsEnforcer — restricts the function selector(s).
- NativeTokenStreamingEnforcer — linear streaming cap on native value.
- NativeTokenPeriodTransferEnforcer — per-period cap on native value.
- ERC20StreamingEnforcer / ERC20PeriodTransferEnforcer — same for ERC-20.
- TimestampEnforcer — timestamp expiry.
Each new permission type maps to a fixed combination of these enforcers. No new Solidity is required.
Where the work sits:
- ERC-7715 schema definition — add the new type strings and validate data shapes.
- Permission handler in MetaMask/snap-7715-permissions — translate each new permission type into the corresponding caveat composition: [AllowedTargets, AllowedMethods, NativeTokenStreaming|NativeTokenPeriodTransfer (or ERC-20 variants), Timestamp].
- Permission picker UI in the Snap — render a human-readable confirmation. Suggested copy: "This dapp can call join(uint8) on 0x1234…ABCD and spend up to 100 POL over 12 hours."
- Smart Accounts Kit SDK surface — add the permission types to the toolkit's TypeScript types and the docs page at docs.metamask.io/smart-accounts-kit/reference/advanced-permissions/permissions/.
- signature-controller validateDelegation — no behavior change required. Once the new permission types ship, the resulting delegations will carry a recognized decodedPermission and pass validation through the existing Advanced Permissions path. The current rejection rule in packages/signature-controller/src/utils/validation.ts → validateDelegation() stays as-is.
Edge cases to handle in the schema validator:
- selectors must be an array of 4-byte hex strings; reject longer or shorter values.
- target must be a valid checksummed address.
- For the streaming variant: if maxAmount is omitted, default behavior should match the existing native-token-stream (no upper cap beyond the streaming rate × time elapsed).
- For the periodic variant: periodDuration must be > 0 and periodAmount > 0.
- selectors array length should be capped (suggest 8) to keep the confirmation UI readable.
Acceptance Criteria
- The five new permission types — native-token-function-call-stream, native-token-function-call-periodic, erc20-token-function-call-stream, erc20-token-function-call-periodic, and (optional) erc20-token-function-call-revocation — are accepted by wallet_grantPermissions.
- Each permission type produces an ERC-7710 delegation with the corresponding caveat composition listed under Technical Details.
- The permission picker UI renders target address, selector(s) (with human-readable function signature where resolvable), amount cap, and time window.
- isAdjustmentAllowed: true lets the user adjust amount, duration, expiry, and start time before approval — but not target or selectors. Target/selector restrictions are part of the security guarantee and must not be loosened by user adjustment.
- Existing native-token-stream / native-token-periodic / ERC-20 permission types continue to behave unchanged.
- Reference docs at docs.metamask.io/smart-accounts-kit/reference/advanced-permissions/permissions/ are updated with parameter tables and code examples in the same shape as existing entries.
- TypeScript types in the Smart Accounts Kit SDK are exported and discoverable through autocomplete.
Scenario 1: granting a contract-scoped streaming permission
- GIVEN a MetaMask user is upgraded to a Smart Account
- WHEN a dapp calls wallet_grantPermissions with type: "native-token-function-call-stream", target: Game, selectors: ["0xcb3e9b84"], a streaming rate, and a maxAmount cap
- THEN the MetaMask permission picker shows a human-readable confirmation including target, function name, streaming rate, and cap
- AND the user can approve, modify amount/duration, or reject
- AND on approval the dapp receives a permission context redeemable through the Delegation Manager
Scenario 2: redeeming within scope
- GIVEN a session account holds a granted native-token-function-call-stream permission scoped to Game.join(uint8)
- WHEN the session account submits a UserOp that calls Game.join(uint8) with a native value within the streaming cap
- THEN the Delegation Manager validates and the call executes successfully
Scenario 3: rejecting out-of-scope target
- GIVEN the same granted permission as Scenario 2
- WHEN the session account attempts to call any contract other than Game, OR calls Game with a different selector
- THEN the redeem reverts via AllowedTargetsEnforcer or AllowedMethodsEnforcer
Scenario 4: rejecting over-cap value
- GIVEN the same granted permission as Scenario 2
- WHEN the session account attempts to call Game.join(uint8) with a native value that exceeds the currently unlocked streaming amount
- THEN the redeem reverts via NativeTokenStreamingEnforcer
Scenario 5: schema rejection
- GIVEN a dapp calls wallet_grantPermissions with a malformed data payload (e.g. target is not an address, selectors is empty, selectors contains non-4-byte hex, periodDuration ≤ 0)
- THEN the request is rejected with a clear validation error before reaching the permission picker
Scenario 6: existing permission types unaffected
- GIVEN dapps already integrated against native-token-stream, native-token-periodic, or any ERC-20 permission type
- WHEN those dapps continue to issue the same wallet_grantPermissions payloads after this change ships
- THEN behavior is byte-identical to today (no regressions)
References
Description
Add a new family of Advanced Permission (ERC-7715) types that combine a target/selector restriction with a native-token (or ERC-20) value cap. The currently supported permission types — native-token-stream, native-token-periodic, erc20-token-stream, erc20-token-periodic, erc20-token-revocation — only express value caps and timing, with no way to restrict the call target or function selector.
This schema gap blocks any session-execution pattern that must call a specific contract method instead of transferring raw value (gaming, DeFi strategy bots, auto-rebalance agents, scheduled liquidations, vesting claims, on-chain voting agents, etc.). Without target/selector restrictions, a leaked session key can spend the value cap to an arbitrary recipient, which is unacceptable for any production-grade agent or session-key flow.
The companion option — constructing a custom ERC-7710 delegation with AllowedTargets + AllowedMethods + streaming caveats — is intentionally blocked at the MetaMask Extension signing layer (External signature requests cannot sign delegations for internal accounts.), so the only path forward for general dapps is via Advanced Permissions. That makes filling this schema gap necessary, not optional, for the use cases above.
Proposed new permission types:
// Native token, streaming
{
type: "native-token-function-call-stream",
data: {
target: Address,
selectors: Hex[], // 4-byte function selectors
amountPerSecond: bigint,
initialAmount?: bigint,
maxAmount?: bigint,
startTime?: number,
justification?: string
}
}
// Native token, periodic
{
type: "native-token-function-call-periodic",
data: {
target: Address,
selectors: Hex[],
periodAmount: bigint,
periodDuration: number,
startTime?: number,
justification?: string
}
}
ERC-20 equivalents (erc20-token-function-call-stream, erc20-token-function-call-periodic) for token-paid contract calls in the same shape.
Technical Details
Onchain primitives — already shipped, no new contract work needed.
The Delegation Framework already includes the audited caveat enforcers required to express this permission shape:
Each new permission type maps to a fixed combination of these enforcers. No new Solidity is required.
Where the work sits:
Edge cases to handle in the schema validator:
Acceptance Criteria
Scenario 1: granting a contract-scoped streaming permission
Scenario 2: redeeming within scope
Scenario 3: rejecting out-of-scope target
Scenario 4: rejecting over-cap value
Scenario 5: schema rejection
Scenario 6: existing permission types unaffected
References
https://community.metamask.io/t/advanced-permissions-cannot-express-target-selector-native-value-cap-request-for-a-new-permission-type/31410