Permaweb Payment Protocol

Goal

Describe a protocol that enables payment channels to incentivize a variety services in the Arweave ecosystem.

Mechanism Design

  1. Clients enumerate available service providers.
  2. Clients request service prices from providers.
  3. Clients make deposits at cheapest and most responsive service providers.
  4. Clients make service requests to providers holding deposits.
  5. Service providers charge service fees against the clients deposit.
  6. Clients score service providers based on reliability and responsiveness.
  7. Service providers compete on price, reliability, and responsiveness to serve clients.

Rates

Service providers may publish their services and corresponding rates via the rates endpoint.

GET
http://arweave.net/rates

The following is an example response body from the /rates endpoint.

{
 "endpoints": [ {
   "endpoint":"/graphql",
   "modSeq": 1,
   "rates": {
     "description": "Price per block queried",
     "arweave": {
       "AR": {
         "price":"10000",
         "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
       },
       "VRT": {
         "price":"1",
         "contractId":"aLemOhg9OGovn-0o4cOCbueiHT9VgdYnpJpq7NgMA1A",
         "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
       }
     },
     "external": {
       "url":"https://example.com/deposit.html"
     },
     "polygon": {
       "MATIC": {
         "price":"11010101",
         "address": "0x0e2Db4C482B9fd5809f5bF48d04A3eE1a9D73D96"
       }
     },
     "solana": {
       "SOL": {
         "price":"111101010",
         "address":"ApFWL3dtpFX8tU15uPzWnvfeQRtompvoAs9DNEnQH6DL"
       }
     }
   }
 }, {
   "endpoint":"/tx/{id}/{field}",
   "modSeq": 3,
   "rates": {
     "description": "Price per request",
     "arweave": {
       "AR": {
         "price":"10000",
         "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
       }
     }
   }
 }, {
   "endpoint":"/tx/{id}",
   "modSeq": 1,
   "rates": {
     "description": "Price per request",
     "arweave": {
       "AR": {
         "price":"10000",
         "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
       }
     }
   }
 },{
   "endpoint":"/chunk/{offset}",
   "modSeq": 5,
   "rates": {
     "description": "Price per byte of data",
     "arweave": {
       "price":"100000",
       "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
     }
   }
 }, {
   "endpoint":"/{txid}",
   "modSeq": 2,
   "rates": {
     "arweave": {
       "price":"100000",
       "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
     }
   }
 }]
}

Schema

The root object contains an array of Service Endpoint Config, each one representing the pricing and metadata for a particular service endpoint.

{
  "endpoints": [ … ]
}

Service Endpoint Config

Each element of the endpoints array is an instance of a Service Endpoint Config. Each config describes the HTTP API service endpoint along service rates (cost).

{
   "endpoint":"/graphql",
   "modSeq": 1,
   "rates": { … }
}
Property Required Description
endpoint required The path to the endpoint that is providing service.
modSeq required The modification sequence number for the endpoint config.
Enables clients to cache rates for an endpoint and be directed to requery rates only when the underlying rates are updated.
rates required A Rates Config object describing the prices for service at this endpoint.

Rates Config

Each Service Endpoint Config publishes an array of Rates Config to describe service pricing. Rate Configs allow for prices to be listed in a variety of network tokens, provided the service operator maintains a wallet native to that network.

  "rates": {
     "description": "Price per block queried",
     "arweave": { … },
     "polygon": { … },
     "solana": { … }
  }

Network Price Config

Describes the price of service denominated in a token native to the network. Also provides the wallet address to transfer tokens to in order to fun a deposit.

"arweave": {
   "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
   "AR": {
     "price":"10000",
   },
   "VRT": {
     "price":"1",
     "contractId":"aLemOhg9OGovn-0o4cOCbueiHT9VgdYnpJpq7NgMA1A",
     "address":"89tR0-C1m3_sCWCoVCChg4gFYKdiH5_ZDyZpdJ2DDRw"
   }
 },

In this example the arweave Network Price Config supports payment in the native token AR as well as a PST token VRT. VRT is managed by a smartweave contract so that contractId is listed as well. For each token the corresponding price for service is listed.

Property Description
price The cost of service (described in the Rates Config description property).
In the case of arweave, the price is delineated in Winstons (atomic units).
address The wallet address to receive the service payment.
contractId In the case of a token managed by a smart contract like a PST or ERC-20, this specifies the contract ID.
NOTE: price is always denominated in the smallest divisible unit of the token, otherwise known as atomic units

External Price Config

For “off chain” value transfers (like credit card payments or other incentivized channels) the price config simply provides a URL of a page to make the payment.

 "external": {
    "url":"https://example.com/deposit.html"
  },

Service Requests

Requesting service from a Permaweb Payments Protocol (P3) endpoint follows a standard HTTP API request format. However, to properly identify which wallet to charge for the service, requests must also include some P3 specific HTTP headers.

Request Headers

Header Required Description
anchor (optional) Nonce that can be optionally provided and optionally implemented by the service endpoint to prevent replay attacks.
modSeq (optional) The caller can specify the modSeq of the Endpoint Config they have cached locally and are expecting to be charged.
price (optional) The <network>/<token> of the Price Config to use. Defaults to arweave/AR.
timeout (optional) Number of milliseconds the client will wait before timing out the request. This enables clients to indicate a willingness to wait longer for responses when the data is important (and not score the provider poorly as a result)
endpoint required The value of the endpoint field in the ServiceEndpointConfig e.g. "/graphql"
address required The wallet address of the deposit to charge for service.
signature required The request headers endpoint + address + modSeq + price + anchor + timeout are concatenated, SHA-256 hashed and the hash is signed by the private key of the wallet indicated by address. (if optional parameters are missing they are dropped)

HTTP Response Codes

Code Description
🟢 200 OK
🔴 400 Bad Request - invalid P3 headers/header values
🔴 402 Bad Request - Payment Required - Not enough funds on deposit
🟡 428 Precondition Required - modSeq is out of date
🟡 429 Too many requests - and/or Rate limited

Response Headers

When the service endpoint responds with data, it will include the following HTTP headers in the response.

Deposits

By default any wallet posting a “deposit” transaction to an address specified by a Price Config will be credited with the deposit and able to sign P3 request headers to bill against it.

Optionally, a deposit transaction may specify an array of wallet entries to be credited with a portion of the deposit. This is accomplished by adding a “addresses” Custom Tag to the transaction with the following entry format, a JSON array of {<address>:<amount>} entries.

"addresses": “[
   { "79pOFCwHmdgiqcl6blGFY45eS87V-OkiPspLvRCrRq4" : 100 },
   { "NoHbaZ9ZchidleZNdHYHb2TFV4puCBJTiESZ07dPXJg" : 50 },
   { "xo6_3x3pdVnRc6fKPduoUtK3M3ABF2-V7iUilHMCy3I" : 50 }
]”

If the total of the amount values does not exactly match the number of tokens being deposited, then the amount‘s will be averaged and the deposit split amongst the wallets proportionally.

In the case where a wallets list is provided, the owner of the transaction must include their own wallet address in the wallets list if they wish it to be credited with a portion of the deposit.

Balances

Enables clients to check their balance with a given service provider to verify pricing and determine when to "top up" their deposit.

GET
http://arweave.net/balance/{address}/{network}/{token}

Note: While Rate Configs are unique to each service endpoint, the balance on deposit for a given wallet address is shared across all service endpoints. There is no protocol defined means to limit deposits to a single service endpoint. If the provider has a deposit for the client wallet it will bill against that deposit regardless of what service is requested.

Charging for Service

When a gateway bills a client for service, it deducts that amount from the local deposit ledger it maintains for client addresses. This way the gateway can more efficiently rollup fees to a single transaction at day's end, or whatever interval makes sense for the gateway operator.

As long as the Balance endpoint is accurate, this should be sufficient for clients.