Skip to content

feat: add delegated signing support to PayerRegistry with authorize/revoke/query functions#259

Open
tantodefi wants to merge 1 commit intoxmtp:mainfrom
tantodefi:feat/delegated-signing
Open

feat: add delegated signing support to PayerRegistry with authorize/revoke/query functions#259
tantodefi wants to merge 1 commit intoxmtp:mainfrom
tantodefi:feat/delegated-signing

Conversation

@tantodefi
Copy link

@tantodefi tantodefi commented Feb 4, 2026

Add Delegated Signing Support to PayerRegistry

Summary

This PR implements the smart contract portion of user-funded messages via delegated signing (xmtp/xmtpd#1599). This allows XMTP users to authorize gateways to sign payer envelopes on their behalf, enabling users to pay for their own message sending rather than relying on gateway operators.

Problem

Currently, message fees are always charged to whoever signs the PayerEnvelope (the gateway). 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.

Solution

Users can now authorize gateways on-chain to act as delegates:

User → PayerRegistry.authorize(gatewayAddress, expiry)
Gateway → signs with own key + delegation info → Network validates → charges user's balance

Changes

New Struct:

struct Delegation {
    bool isActive;
    uint64 expiry;      // 0 = no expiry
    uint64 createdAt;
}

New Events:

  • DelegationAuthorized(address indexed payer, address indexed delegate, uint64 expiry)
  • DelegationRevoked(address indexed payer, address indexed delegate)

New Errors:

  • ZeroDelegate()
  • DelegationAlreadyExists()
  • DelegationDoesNotExist()
  • DelegationExpiryInPast()

New Functions:

  • authorize(address delegate, uint64 expiry) - Grant delegation to a gateway
  • revoke(address delegate) - Revoke delegation
  • isAuthorized(address payer, address delegate) → bool - Check if delegation is valid and not expired
  • getDelegation(address payer, address delegate) → Delegation - Get delegation details

Testing

  • Added 12 new tests covering authorization, revocation, expiry, and edge cases
  • All 103 PayerRegistry tests passing
  • Slither analysis shows no new issues

Related

Summary by CodeRabbit

Release Notes

  • New Features
    • Added delegation system to manage signatory authority delegation from payers to delegates
    • Payers can authorize delegates with optional expiration dates
    • Revoke delegations at any time
    • Query whether a delegate is currently authorized
    • Retrieve full delegation details including expiration and creation timestamps

@tantodefi tantodefi requested a review from a team as a code owner February 4, 2026 19:29
@octane-security-app
Copy link

Summary by Octane

New Contracts

No new contracts were added.

Updated Contracts

  • PayerRegistry.sol: The smart contract now supports payer delegations, enabling authorization, revocation, and delegation queries.

🔗 Commit Hash: 68aec14

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Walkthrough

A new delegation system was added to PayerRegistry, enabling payers to authorize and revoke delegates with optional expiry times. The system includes authorization checks, revocation capabilities, and query functions to verify delegation status and retrieve delegation details.

Changes

Cohort / File(s) Summary
Interface Definitions
src/settlement-chain/interfaces/IPayerRegistry.sol
Added Delegation struct with isActive, expiry, and createdAt fields. Introduced delegation lifecycle events (DelegationAuthorized, DelegationRevoked), error types (ZeroDelegate, DelegationAlreadyExists, DelegationDoesNotExist, DelegationExpiryInPast), and public functions for authorization management (authorize, revoke) and delegation queries (isAuthorized, getDelegation).
Implementation Logic
src/settlement-chain/PayerRegistry.sol
Implemented delegation storage via nested mapping (payer → delegate → Delegation). Added authorize() to create delegations with validation for zero delegates and duplicate entries. Added revoke() to deactivate delegations. Implemented isAuthorized() to check active non-expired delegations and getDelegation() to fetch delegation data.
Test Coverage
test/unit/PayerRegistry.t.sol
Added comprehensive unit tests covering authorization edge cases (zero delegate, paused state, past expiry, existing delegation), successful authorization scenarios (with/without expiry), revocation flows, expiry checks, and reauthorization after revocation with updated createdAt.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

enhancement

Suggested reviewers

  • neekolas
  • fbac
  • mkysel

Poem

🐰 A payer hops forth with a delegation to grant,
whoosh — expiry checked, no duplicates to slant,
Revoke when you must, query the rest,
Authorization chains put to the test! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding delegated signing support to PayerRegistry with the core functions (authorize/revoke/query).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@macroscopeapp
Copy link

macroscopeapp bot commented Feb 4, 2026

Add delegated signing to PayerRegistry and implement PayerRegistry.authorize, PayerRegistry.revoke, and PayerRegistry.isAuthorized with per-payer delegate storage and expiry validation

Introduce a delegations mapping, new authorization and revocation flows with events and errors, and a query API via isAuthorized and getDelegation in PayerRegistry.sol and its interface in IPayerRegistry.sol.

📍Where to Start

Start with PayerRegistry.authorize and PayerRegistry.revoke in PayerRegistry.sol, then review the Delegation types and events in IPayerRegistry.sol.


Macroscope summarized 68aec14.

@octane-security-app
Copy link

Overview

Vulnerabilities found: 1                                                                                
Severity breakdown: 1 Low
Warnings found: 2                                                                                

Detailed findings

src/settlement-chain/PayerRegistry.sol

  • Expired delegations retain active flag in PayerRegistry causes re-authorization failures and potential downstream misuse. See more

Warnings

src/settlement-chain/PayerRegistry.sol

  • isAuthorized view ignoring pause in PayerRegistry causes continued off-chain delegated actions during pause and post-unpause charges. See more

test/upgrades/PayerRegistryUpgrade.fork.t.sol

  • Missing delegation state checks in PayerRegistry upgrader/test causes undetected delegation state loss across upgrades. See more

🔗 Commit Hash: 68aec14
🛡️ Octane Dashboard: All vulnerabilities

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.

1 participant