Conversation
…hen check for deprecatedEnvVars
…miting, and envelope validation
Add delegated signing to payer service and wire unified contract config loading with standardized
|
| return fetchURL(path) | ||
| } | ||
|
|
||
| // Handle file:// URLs |
There was a problem hiding this comment.
🟡 Medium
config/loader.go:90 file:// handling with strings.TrimPrefix is brittle and breaks Windows drive-letter and UNC paths. Suggest using url.Parse and deriving a platform-correct path (via u.Path/u.Host and filepath) before os.ReadFile.
🚀 Want me to fix this? Reply ex: "fix it for me".
| options.AppChain.IdentityUpdateBroadcasterAddress = config.IdentityUpdateBroadcaster | ||
| options.AppChain.IdentityUpdateBroadcasterAddress = loaded.AppChain.IdentityUpdateBroadcasterAddress | ||
| } | ||
| if options.AppChain.ChainID == 0 || options.AppChain.ChainID == 31337 { |
There was a problem hiding this comment.
🟡 Medium
config/validation.go:220 Treating 31337 as “unset” in mergeContractsOptions causes explicit ChainID=31337 (app and settlement) to be overwritten by loaded config. Suggest tracking whether the flag/value was explicitly set (e.g., a separate boolean) and only treating 0 as unset, or remove the 31337 check. If intentional, document this behavior.
🚀 Want me to fix this? Reply ex: "fix it for me".
| if !p.IsDelegated() { | ||
| return nil | ||
| } | ||
| addr := common.BytesToAddress(p.proto.DelegatedPayerAddress) | ||
| return &addr |
There was a problem hiding this comment.
🟢 Low
envelopes/payer.go:84 Consider validating that DelegatedPayerAddress is exactly 20 bytes before calling BytesToAddress. Otherwise, malformed inputs get silently padded/cropped, potentially charging fees to the wrong address.
| if !p.IsDelegated() { | |
| return nil | |
| } | |
| addr := common.BytesToAddress(p.proto.DelegatedPayerAddress) | |
| return &addr | |
| if !p.IsDelegated() || len(p.proto.DelegatedPayerAddress) != 20 { | |
| return nil | |
| } | |
| addr := common.BytesToAddress(p.proto.DelegatedPayerAddress) | |
| return &addr |
🚀 Want me to fix this? Reply ex: "fix it for me".
|
I like the idea at a high level. Think we'll have to take some time to digest it before adopting it. Love the thinking here, though. Being able to pay your own way without having to sign every message |
Implement Delegated Signing for User-Funded Messages
Summary
This PR implements the backend portion of user-funded messages via delegated signing (#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
The current payment architecture has a fundamental limitation:
PayerEnvelopesigned withXMTPD_PAYER_PRIVATE_KEYUsers cannot pay for their own messages even if they've funded an on-chain payer balance.
Solution
Users authorize gateways on-chain, then the gateway signs on their behalf:
Changes
New Package:
pkg/delegationProvides delegation verification with caching:
Caching Strategy (Time-based TTL):
Trade-offs:
Protocol Changes
PayerEnvelopenow includes optional delegation field:When set:
PayerSignatureis from the gateway (delegate)DelegatedPayerAddress(user)Envelope Validation (
pkg/envelopes/payer.go)New helper methods:
IsDelegated() bool- Check if using delegated signingGetDelegatedPayerAddress() *common.Address- Get delegated payer if setGetActualPayer() (*common.Address, error)- Get address to charge (resolves delegation)Gateway Payer Service (
pkg/api/payer/service.go)signClientEnvelope()now supports delegated signing:delegatedPayerAddressis provided, verify delegation on-chain (with caching)DelegatedPayerAddressin the envelopeDual Rate Limiter (
pkg/ratelimiter)New
DualRateLimiterinterface applies limits in parallel:This enables:
When a delegated request comes in, both limits must pass.
Files Changed
pkg/constants/constants.gopkg/delegation/delegation.gopkg/delegation/chain_verifier.gopkg/delegation/delegation_test.gopkg/api/payer/service.gopkg/envelopes/payer.gopkg/ratelimiter/interface.gopkg/ratelimiter/dual_limiter.gopkg/ratelimiter/dual_limiter_test.goTesting
Future Work (SDK Changes Required)
The following SDK changes are needed to fully enable user-funded messages:
Client SDK:
authorizeGateway(gatewayAddress, expiry?)- Grant delegationrevokeGateway(gatewayAddress)- Revoke delegationgetPayerBalance(address)- Query user's payer balancedeposit(amount)- Deposit funds to payer registryAPI Endpoints:
POST /payer/authorize- Create delegationDELETE /payer/authorize/{gateway}- Revoke delegationGET /payer/balance- Get user balanceGET /payer/delegations- List delegationsSecurity Considerations
Related