You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Defines the concept of Fee Sponsoring with third-party Merchant Accounts. A Merchant Account is a Porto Account that lets businesses or creators accept crypto payments, sponsor fees, subsidize payments, etc. A Merchant Account can be created with the porto onboard CLI command, funded by the Merchant entity, and then consumed by a lightweight Merchant Server Handler that signs payment signatures for sponsored actions. The Porto SDK only needs to be told the Merchant Server URL; thereafter it can orchestrate two-party signing (merchant + user) and submit prepared actions to the RPC Server without additional user friction.
sequenceDiagram
actor U as User
participant P as Porto Dialog
participant M as Merchant Server
participant S as RPC Server
autoNumber
U ->> P: request payment
P ->> M: prepare payment
M ->> M: inject fee payer address<br>into payment
M ->> S: prepare payment
S -->> M: digest
M ->> M: ✍️ sign digest
M -->> P: digest
P ->> P: ✍️ sign digest
P ->> S: broadcast signed payment
S -->> P: identifier
P -->> U: identifier
Loading
Motivation
Porto exposes no interfaces or surfaces for merchants to be able to sponsor fees for their users, thus users are forced to pay the fee.
Specification & Design
Terms
Entity
Description
Merchant Account
A Porto account owned by the App Dev. Holds native tokens to pay fees, and an admin P-256 key used only for payment-signature signing.
Merchant Server
Merchant server that sits between the User and RPC Server, and intercepts JSON-RPC requests. It can intercept SDK wallet_prepareCalls, add a payment signature, and forward it on to RPC Server.
RPC Server
Existing Porto Relay service—no protocol changes required beyond recognizing the optional paymentSignature field.
Create Merchant Account
Flow
sequenceDiagram
actor U as Merchant
participant C as CLI
participant D as Dialog
autoNumber
U ->> C: `porto onboard`
C ->> C: serverKey = Key.createSecp256k1()
C ->> D: wallet_connect({ <br>createAccount: true, <br>grantAdmin: serverKey })
D ->> D: register passkey
D ->> D: create account <br>+ authorize(passkey, serverKey)
D -->> C: address
C ->> D: wallet_addFunds({ address })
D ->> D: fund account
D -->> C:
C --> U: (address, <br>serverKey.privateKey)
Loading
Merchant creates a Porto Account via porto onboard
Porto CLI will internally generate a new admin key pair to use on the server.
Porto CLI will request to create an account with an admin key.
The Porto Dialog will register a passkey to authorize on the account
5-6. An account will be created for the merchant with 2 admin keys: their passkey (for account management), and their server key (for server usage)
7-9. Merchant will be prompted to fund the account
CLI returns the address and privateKey to be used with MerchantRpc.requestHandler (below).
CLI Definition
# Create a new merchant account
porto onboard
porto o
Fee Sponsoring
Flow
sequenceDiagram
actor U as User
participant P as Porto Dialog
participant M as Merchant Server
participant S as RPC Server
autoNumber
U ->> P: wallet_sendCalls(calls)
P ->> M: wallet_prepareCalls(calls)
M ->> S: wallet_prepareCalls(calls,<br>feePayer=merchantAddress)
S -->> M: (context, digest)
M ->> M: context.paymentSignature = <br>merchant_sk.sign(digest)
M -->> P: (context, digest)
P ->> P: signature = passkey.sign(digest)
P ->> S: wallet_sendPreparedCalls(context, signature)
S -->> P: id
P -->> U: id
Loading
User invokes Porto to perform an arbitrary action.
Porto will prepare the action using the Merchant Server.
The Merchant Server will inject its address as the feePayer for the action, and send it off to the RPC Server
Merchant Server signs over the digest returned from the RPC Server, and populates context.paymentSignature
The User signs over the digest with their passkey.
The signed and prepared action is sent back to the relay.
User gets back the id.
Merchant Server
Porto exports an RPC Handler compatible with the Web API Request interface and Node.js RequestListener. These can be plugged into new or existing backends. We need to support both the Web API and Node.js interfaces as there are many backend frameworks that use both (Workers, Next.js Bun, Hono, etc exclusively support Request; Node.js, Express, etc exclusively support RequestListener) .
A merchant capability will be exposed on a per-call basis. Consumers can define a merchant.rpcUrl value on wallet_sendCalls / wallet_prepareCalls to enable the ability for a Merchant Server to be used for fee sponsoring and other merchant-related activities (e.g. payment subsidy)
A merchant option is exposed on Porto.create and porto Connector, to set merchant-related configuration:
typeConfig={merchant?: {/** Merchant RPC URL. */rpcUrl: string,/** Whether the Merchant should sponsor this intent. */sponsor: (params: wallet_sendCalls.Parameters)=>bool}}Porto.create({merchant: {rpcUrl: 'https://merchant.com/rpc'}})
Rationale
TODO
Backwards Compatibility
No breaking changes.
Security Considerations
The initial implementation does expose the private key in plaintext to stdout in the CLI, which is not ideal. It is recommended for consumers to store this key in a secure environment (e.g. gitignored .env file, secured secret storage, etc). Fast follows of this RFC will explore more secure patterns of secure key storage that could involve: option to inject secrets via the Vercel or Wrangler CLIs, AWS KMS, JSON Keystores, etc.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Third-party Fee Sponsoring
Abstract
Defines the concept of Fee Sponsoring with third-party Merchant Accounts. A Merchant Account is a Porto Account that lets businesses or creators accept crypto payments, sponsor fees, subsidize payments, etc. A Merchant Account can be created with the
porto onboardCLI command, funded by the Merchant entity, and then consumed by a lightweight Merchant Server Handler that signs payment signatures for sponsored actions. The Porto SDK only needs to be told the Merchant Server URL; thereafter it can orchestrate two-party signing (merchant + user) and submit prepared actions to the RPC Server without additional user friction.sequenceDiagram actor U as User participant P as Porto Dialog participant M as Merchant Server participant S as RPC Server autoNumber U ->> P: request payment P ->> M: prepare payment M ->> M: inject fee payer address<br>into payment M ->> S: prepare payment S -->> M: digest M ->> M: ✍️ sign digest M -->> P: digest P ->> P: ✍️ sign digest P ->> S: broadcast signed payment S -->> P: identifier P -->> U: identifierMotivation
Porto exposes no interfaces or surfaces for merchants to be able to sponsor fees for their users, thus users are forced to pay the fee.
Specification & Design
Terms
wallet_prepareCalls, add a payment signature, and forward it on to RPC Server.paymentSignaturefield.Create Merchant Account
Flow
sequenceDiagram actor U as Merchant participant C as CLI participant D as Dialog autoNumber U ->> C: `porto onboard` C ->> C: serverKey = Key.createSecp256k1() C ->> D: wallet_connect({ <br>createAccount: true, <br>grantAdmin: serverKey }) D ->> D: register passkey D ->> D: create account <br>+ authorize(passkey, serverKey) D -->> C: address C ->> D: wallet_addFunds({ address }) D ->> D: fund account D -->> C: C --> U: (address, <br>serverKey.privateKey)porto onboardkeypair to use on the server.key.5-6. An account will be created for the merchant with 2 admin keys: their passkey (for account management), and their server key (for server usage)
7-9. Merchant will be prompted to fund the account
addressandprivateKeyto be used withMerchantRpc.requestHandler(below).CLI Definition
# Create a new merchant account porto onboard porto oFee Sponsoring
Flow
sequenceDiagram actor U as User participant P as Porto Dialog participant M as Merchant Server participant S as RPC Server autoNumber U ->> P: wallet_sendCalls(calls) P ->> M: wallet_prepareCalls(calls) M ->> S: wallet_prepareCalls(calls,<br>feePayer=merchantAddress) S -->> M: (context, digest) M ->> M: context.paymentSignature = <br>merchant_sk.sign(digest) M -->> P: (context, digest) P ->> P: signature = passkey.sign(digest) P ->> S: wallet_sendPreparedCalls(context, signature) S -->> P: id P -->> U: idfeePayerfor the action, and send it off to the RPC Servercontext.paymentSignatureid.Merchant Server
Porto exports an RPC Handler compatible with the Web API
Requestinterface and Node.jsRequestListener. These can be plugged into new or existing backends. We need to support both the Web API and Node.js interfaces as there are many backend frameworks that use both (Workers, Next.js Bun, Hono, etc exclusively supportRequest; Node.js, Express, etc exclusively supportRequestListener) .API
Examples
API Changes
Merchant Capability
A
merchantcapability will be exposed on a per-call basis. Consumers can define amerchant.rpcUrlvalue onwallet_sendCalls/wallet_prepareCallsto enable the ability for a Merchant Server to be used for fee sponsoring and other merchant-related activities (e.g. payment subsidy)Merchant Config Options
A
merchantoption is exposed onPorto.createandportoConnector, to set merchant-related configuration:Rationale
TODO
Backwards Compatibility
No breaking changes.
Security Considerations
The initial implementation does expose the private key in plaintext to stdout in the CLI, which is not ideal. It is recommended for consumers to store this key in a secure environment (e.g. gitignored
.envfile, secured secret storage, etc). Fast follows of this RFC will explore more secure patterns of secure key storage that could involve: option to inject secrets via the Vercel or Wrangler CLIs, AWS KMS, JSON Keystores, etc.Beta Was this translation helpful? Give feedback.
All reactions