ERC Permissions

Draft primitive for simple delegated execution.

Apple-ish coder leet edition, but for permissions

auth X to do Y.

Not broad custody. Not app-specific auth spaghetti. Just a narrow, legible primitive: let an operator call one function on one target, with optional EIP-712 signatures for cleaner one-off execution.

Example

“Auth bot to rebalance my LP wrapper.”

PermissionKey({
  owner: alice,
  operator: bot,
  target: lpWrapper,
  selector: IUniV3LPWrapper.rebalance.selector
})

// auth bot to rebalance Alice's wrapper position

owner

alice

operator

bot

ability

rebalance()

Principles

Simplicity first. No frills in the primitive.

The point is not to encode every policy on earth. The point is to make the core auth edge dead simple, easy to reason about, and easy for contracts to adopt.

One obvious permission model

Owner, operator, target, selector. That is the shape. If you can explain it in one sentence, wallets and developers can actually use it.

Complexity stays in the registry

Typed data verification, nonces, one-off execution permits, replay protection. Integrating contracts should mostly just use a modifier.

Protocol semantics stay local

The registry answers whether a caller is allowed. The wrapper or protocol still decides what rebalance, compound, or migrate actually means.

Simple DX

Three examples tell the whole story.

Grant a standing right. Protect a target with one modifier. Or use a one-off signed execution permit when you want tighter control.

1 · standing grant

Permanent auth when ongoing automation makes sense

registry.grant({
  operator: bot,
  target: address(lpWrapper),
  selector: IUniV3LPWrapper.rebalance.selector
});
2 · integrator

Minimal contract-side integration

modifier onlyAuthorized(address owner) {
  if (msg.sender != owner) {
    registry.requireAuthorizedCall(owner, msg.sender, address(this), msg.sig);
  }
  _;
}
3 · one-off

Permit-style exact execution when you want less ambient authority

registry.executeWithPermit(
  ExecutionPermit({
    owner: alice,
    operator: bot,
    target: address(lpWrapper),
    selector: IUniV3LPWrapper.compound.selector,
    calldataHash: keccak256(data),
    nonce: 7,
    deadline: deadline
  }),
  signature,
  data
);

LP hypothetical

A hypothetical Uniswap LP wrapper is where this becomes obviously useful.

Imagine a wrapper that adds better range management, fee compounding, and automation around v3 or v4 positions. Today you often end up with awkward custody tradeoffs or app-specific auth. This primitive gives a cleaner middle ground.

Hypothetical v3 wrapper

Better features without handing over blanket control

The wrapper can expose higher-level features like rebalance and collect-and-compound. The operator only gets permission to call those methods — not arbitrary withdrawal powers.

contract UniV3LPWrapper is PermissionedTarget {
  function rebalance(
    address owner,
    uint256 tokenId,
    RebalanceParams calldata params
  ) external onlyAuthorized(owner) {
    // unwrap old range
    // mint new range
    // keep custody model local to the wrapper
  }

  function collectAndCompound(
    address owner,
    uint256 tokenId
  ) external onlyAuthorized(owner) {
    // collect fees
    // add liquidity back in
  }
}
Hypothetical v4 wrapper

Same primitive, more expressive position management

Hook-aware or coordinator-driven products get even more value from narrow delegated calls. The wrapper owns the product semantics. The registry just keeps auth legible.

contract UniV4LPWrapper is PermissionedTarget {
  function rebalancePosition(
    address owner,
    PoolKey calldata pool,
    RebalanceParams calldata params
  ) external onlyAuthorized(owner) {
    // wrapper logic stays local
    // registry only answers: is this caller allowed?
  }
}

Feature upgrade

Auto-rebalancing, fee sweeps, compounding, safety exits. These become wrapper-level product features instead of custody-heavy strategy vault assumptions.

Clear trust surface

You are not trusting a bot with everything. You are trusting it with a small set of wrapper methods that are visible and revocable.

Cleaner wallet UX

A wallet prompt can say: allow bot to call rebalance() on LPWrapper. That is much better than asking users to bless an opaque blob of app-specific logic.