BotanaryBotanary
Delegations

Granting a delegation

How a delegation is built, previewed, signed, and enabled - from intent to an on-chain Smart Sessions grant.

Granting a delegation is a build-then-sign flow, the same as every other state-changing action in Botanary. Nothing is executed or signed on your behalf - the backend only ever hands back an unsigned operation for you to sign.

The grant wizard

The delegation grant flow is a three-step wizard:

Who - pick the delegate (a dApp, an automation, a recurring payment, or a mandate for a hired agent) and its kind.

Budget and scope - set the bounds: for a v1 delegation, budgets, a per-action max, an action count, recipients, and allowed actions; for a mandate, venues and per-venue spend caps.

Review - see the plain-language summary before you sign anything.

Build: from intent to an enforceable policy

Once you submit the wizard, the backend runs a fixed pipeline:

  1. Validate the intent - buildPolicySet checks that what you're asking for can actually be enforced on-chain, and throws if it can't.
  2. Encode the session - buildDelegationSession encodes a real Smart Sessions session from that validated policy.
  3. Render the summary - renderPolicySummary turns the encoded policy - not your raw request
    • into the plain-language summary you review.
  4. Return an unsigned operation - an unsigned enable UserOperation, plus a preview of what would happen if it were relayed right now.

You sign that operation yourself; the backend never holds a key that could sign it for you.

What you can bound (v1 delegations)

FieldWhat it bindsNotes
budgetstotal spend per tokena lifetime cumulative total, never a per-period rate
perActionMaxthe max a single delegated action can moveoptional
maxActionshow many delegated actions can ever be takena lifetime count, never a rate
recipientAllowlistwhich addresses can receive fundssee recipient rules below
allowedActionswhich (contract, function) pairs the delegate can calleach entry must name at least one 4-byte function selector
expiresAtwhen the whole grant lapsesdefaults to 365 days from now if omitted

Recipients follow a specific escalation on-chain:

  • 0 recipients - unbounded (any address may receive funds, within the other bounds).
  • 1 recipient - enforced with a single equality rule.
  • 2 to 16 recipients - enforced with a dedicated allowlist policy.
  • More than 16 - not possible. 16 is the maximum number of recipients Smart Sessions can enforce on one delegation.

A token can only be granted to a delegation if it's independently marked delegatable for that chain - a stricter check than whether the token can simply be sent. Not every sendable token can be delegated.

Mandates (v2): venues and argument rules

A mandate (kind: 'mandate') is built from a different shape of intent: instead of budgets and maxActions, it takes venues and a per-venue perActionMax array.

  • MandateVenue - a (target, selector) pair the mandate may call, with an optional name and argument rules.
  • MandateArgRule - an argument-level bound within a venue: a max value at a given argument offset, or a required exact value. This is how, for example, a job's expected budget gets capped or a provider address gets pinned so the delegate can't self-deal.

Mandates are fail-closed by default: an empty venues list or an empty recipient list denies every call, not "allows all" - the opposite of a v1 delegation's empty-allowlist default. Every real bound a mandate enforces - which venues, which recipients, which per-action caps, which argument rules - lives in the mandate executor's own on-chain storage for that specific mandate, set through dedicated configuration calls rather than as ordinary session policies. The session itself carries exactly one action: a single call into the mandate executor. All of that configuration, plus enabling the session, is sent as one atomic operation - it can't partially apply.

Every call made under a mandate must carry zero native value - a mandate can move tokens, but never attach native currency to a call.

Before you sign

If your account hasn't deployed yet, the grant operation carries the deploy data too - the account self-deploys and the session gets enabled in the same atomic operation. There's no separate "deploy your account first" step.

Once you've signed, relaying enables the delegation: the backend requires a real signature on the exact operation it built (an unsigned operation it never built can't be enabled), then waits a bounded window for a terminal on-chain result before recording the delegation. If that window passes without a final answer, it records the honest state - pending - rather than guessing that it succeeded.

See Delegations for what gets bound and how it's enforced, and Simulation for what that preview step actually checks.

On this page