XIP: delegated signing for user-funded messages


xip:

title: Delegated Signing for User-Funded Messages

description: Enable users to pay for their own message sending by authorizing gateways to sign payer envelopes on their behalf

author: @tantodefi

discussions-to: GitHub · Where software is built

status: Draft

type: Standards

category: Core

created: 2026-02-04


Abstract

This XIP introduces delegated signing, a mechanism that allows XMTP users to pay for their own message sending by authorizing gateway operators to sign payer envelopes on their behalf. Users create on-chain delegation authorizations in the PayerRegistry contract, enabling gateways to include delegation information when signing envelopes. The network then charges the user’s payer balance instead of the gateway’s balance.

Motivation

The current XMTP payment architecture has a fundamental limitation:

  1. Client constructs a message envelope

  2. Gateway wraps it in a PayerEnvelope signed with XMTPD_PAYER_PRIVATE_KEY

  3. Network charges the payer whose key signed the envelope

  4. The payer charged is determined by who signs, not who sent the message

This means users cannot pay for their own messages even if they’ve funded an on-chain payer balance, because the gateway always signs with its own key.

This limitation prevents:

  • Users from having direct control over their messaging costs

  • Business models where users pay per-message

  • Decoupling gateway operation costs from message volume

  • True self-sovereign messaging where users manage their own balances

Delegated signing solves this by allowing users to authorize specific gateways to sign on their behalf while charging the user’s balance.

A proof-of-concept demonstrating user-funded messaging with Aave yield has been implemented:

Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Smart Contract Changes

Delegation Struct

A new struct MUST be added to IPayerRegistry:


struct Delegation {

bool isActive;

uint64 expiry; // 0 = no expiry

uint64 createdAt;

}

Events

The following events MUST be emitted:

  • DelegationAuthorized(address indexed payer, address indexed delegate, uint64 expiry) - Emitted when a delegation is created

  • DelegationRevoked(address indexed payer, address indexed delegate) - Emitted when a delegation is revoked

Errors

The following errors MUST be defined:

  • ZeroDelegate() - Reverts when delegate address is zero

  • DelegationAlreadyExists() - Reverts when creating duplicate delegation

  • DelegationDoesNotExist() - Reverts when revoking non-existent delegation

  • DelegationExpiryInPast() - Reverts when expiry timestamp is in the past

Functions

The following functions MUST be implemented:

authorize(address delegate, uint64 expiry)

Grants delegation authority to a gateway address.

  • MUST revert with ZeroDelegate() if delegate is zero address

  • MUST revert with DelegationAlreadyExists() if delegation already exists

  • MUST revert with DelegationExpiryInPast() if expiry is non-zero and in the past

  • MUST emit DelegationAuthorized event

  • An expiry of 0 MUST indicate no expiration

revoke(address delegate)

Revokes a previously granted delegation.

  • MUST revert with ZeroDelegate() if delegate is zero address

  • MUST revert with DelegationDoesNotExist() if delegation does not exist

  • MUST emit DelegationRevoked event

isAuthorized(address payer, address delegate) → bool

Checks if a delegation is currently valid.

  • MUST return true only if delegation is active AND (expiry is 0 OR expiry > block.timestamp)

  • MUST return false otherwise

getDelegation(address payer, address delegate) → Delegation

Returns the delegation information for a payer/delegate pair.

Protocol Changes

PayerEnvelope Extension

The PayerEnvelope protobuf message MUST include an optional delegation field:


message PayerEnvelope {

bytes unsigned_client_envelope = 1;

Signature payer_signature = 2;

bytes delegated_payer_address = 3; // Optional: 20-byte Ethereum address

}

When delegated_payer_address is set:

  • payer_signature MUST be from the gateway (delegate)

  • Fees MUST be charged to delegated_payer_address (user)

Gateway Behavior

When a gateway receives a request with delegation:

  1. The gateway MUST verify the delegation is valid on-chain (caching is RECOMMENDED)

  2. If valid, the gateway MUST include delegated_payer_address in the PayerEnvelope

  3. If invalid, the gateway SHOULD fall back to gateway payment or reject the request

Node Validation

Nodes receiving envelopes with delegated_payer_address:

  1. MUST verify the delegation is valid on-chain before accepting

  2. MUST charge fees to delegated_payer_address instead of the signer

  3. MUST reject envelopes with invalid or expired delegations

Rate Limiting

Implementations SHOULD apply dual rate limiting for delegated requests:

  1. Gateway limits: Overall throughput cap for the gateway

  2. User limits: Per-user message rate limits

Both limits MUST pass for a delegated request to be allowed.

Caching Considerations

Implementations MAY cache delegation status to reduce on-chain queries.

  • RECOMMENDED default TTL: 5 minutes

  • Implementations MUST check on-chain expiry timestamps in addition to cache TTL

  • Users SHOULD be informed of cache TTL when revoking delegations

Rationale

Why On-Chain Delegation?

Alternative approaches considered:

  1. Signed delegation messages: Users could sign off-chain messages authorizing gateways. This was rejected because:
  • No way to revoke without maintaining state

  • Harder to audit active delegations

  • No expiry enforcement

  1. Gateway-specific tokens: Users could mint tokens for specific gateways. This was rejected due to complexity and gas costs.

  2. Multi-sig envelopes: Require both user and gateway signatures. This was rejected because it requires protocol-level changes to signature verification.

On-chain delegation was chosen because:

  • Clear audit trail

  • Straightforward revocation

  • Expiry enforcement by the protocol

  • Minimal protocol changes required

Why Time-Based Cache TTL?

Alternative caching strategies considered:

  1. Event-based invalidation: Subscribe to contract events. This was rejected for initial implementation due to complexity but RECOMMENDED for future enhancement.

  2. No caching: Always query on-chain. This was rejected due to RPC load and latency concerns.

  3. Infinite cache with explicit invalidation: This was rejected because revocations would never propagate.

Time-based TTL balances:

  • Reduced RPC calls (5 min cache = ~288 calls/day vs 86,400+ without caching)

  • Acceptable revocation latency for most use cases

  • Simple implementation

Why Dual Rate Limiting?

Without user-specific limits, a user with a large balance could monopolize gateway capacity. Dual limiting ensures:

  • Gateway operators can cap total throughput

  • Individual users cannot abuse the system

  • Fair resource allocation across users

Backward Compatibility

This XIP is fully backward compatible:

  • Existing envelopes without delegated_payer_address continue to work unchanged

  • Gateways that don’t support delegation continue to operate normally

  • Users who don’t create delegations are unaffected

  • Nodes MUST support both delegated and non-delegated envelopes

Test Cases

Contract Tests

  1. Authorization: User authorizes gateway, isAuthorized returns true

  2. Revocation: User revokes, isAuthorized returns false

  3. Expiry: Delegation with past expiry returns false for isAuthorized

  4. No expiry: Delegation with expiry=0 never expires

  5. Zero address: authorize(address(0)) reverts with ZeroDelegate

  6. Duplicate: authorize on existing delegation reverts with DelegationAlreadyExists

Backend Tests

  1. Caching: Verify cache hit returns same result without RPC call

  2. Cache expiry: Verify cache miss after TTL triggers RPC call

  3. Delegation validation: Verify gateway correctly validates before signing

  4. Dual rate limiting: Verify both gateway and user limits are enforced

Reference Implementation

Smart Contracts

Backend (xmtpd)

Files Changed

smart-contracts:

  • src/settlement-chain/interfaces/IPayerRegistry.sol

  • src/settlement-chain/PayerRegistry.sol

  • test/unit/PayerRegistry.t.sol

xmtpd:

  • pkg/constants/constants.go

  • pkg/delegation/delegation.go

  • pkg/delegation/chain_verifier.go

  • pkg/delegation/delegation_test.go

  • pkg/api/payer/service.go

  • pkg/envelopes/payer.go

  • pkg/ratelimiter/interface.go

  • pkg/ratelimiter/dual_limiter.go

  • pkg/ratelimiter/dual_limiter_test.go

Security Considerations

Threat Model

Malicious Gateway

A gateway authorized by a user could attempt to drain the user’s balance by sending unauthorized messages.

Mitigation:

  • Users SHOULD only authorize trusted gateways

  • Users SHOULD set reasonable expiry times

  • Users SHOULD monitor their balance

  • Per-user rate limits prevent rapid balance drainage

Revocation Latency

Due to caching, a revoked delegation may remain valid for up to the cache TTL (default 5 minutes).

Mitigation:

  • Users SHOULD wait for cache TTL before assuming revocation is effective

  • High-security deployments SHOULD reduce cache TTL

  • Future implementations SHOULD consider event-based invalidation

Delegation Replay

An attacker could attempt to replay old delegation transactions.

Mitigation:

  • isAuthorized always checks current on-chain state

  • Expiry timestamps are enforced at the contract level

  • Revocation immediately marks delegation as inactive

Balance Exhaustion

A user’s balance could be exhausted by legitimate but excessive usage.

Mitigation:

  • Per-user rate limits cap message throughput

  • SDKs SHOULD provide balance visibility and low-balance warnings

  • Users SHOULD monitor their balance

Recommendations

  1. Expiry times: Set delegation expiry to the minimum necessary duration

  2. Gateway trust: Only authorize gateways operated by trusted parties

  3. Balance monitoring: Implement alerts for low balance conditions

  4. Rate limit tuning: Adjust per-user limits based on expected usage patterns

Copyright

Copyright and related rights waived via CC0.

2 Likes