RWTP stores things like titles, descriptions, images, shipping address, and contact information off-chain. Client applications should use IPFS to store this data.1

An Order contains a uri that must point to a JSON object. That object must contain a minimum of three fields:

interface OrderMetadata {
  // A JSON Schema defining this object
  $type: string;

  // JSON schemas defining accepted metadata for the offer
  $accepts: string[];

  // A publicKey that offerers should encrypt their metadata with
  // This is not the user's Ethereum address.
  $encryptionPublicKey: string;

  // A message that, if signed by the user, can be used to generate
  // the keypair associated with the encryptionPublicKey.
  $scope?: string;

Type and Accepts

The $type field is a URI pointing to a JSON schema defining metadata for the order (such as the title, description, and photos). The $accepts field is an array of JSON schema URIs that define metadata for acceptable offers (such as shipping details, contact information, and names).

$type says "here's what I'm buying or selling". $accepts says "here's how I'd like you to give me an offer".

Example of a $type schema

Given a schema like the following:

  "title": "Simple Ecommerce Sell Order Schema",
  "description": "A public billboard",
  "type": "object",
  "properties": {
    "title": {
      "title": "Title",
      "description": "The main header describing the item",
      "type": "string"
    "description": {
      "title": "Description",
      "description": "A longer, more detailed description of the item",
      "type": "string"
  "required": ["title", "description"]

You could upload metadata like the following:

  "title": "A 1-bedroom condo near a coffee shop",
  "description": "A cute spot in a cool neighborhood./n Next to the Keychannel cafe."

Example of an $accepts schema

Given the following schema:

  "title": "Simple Ecommerce Offer Schema",
  "description": "Payload for buyer's metadata when the request to purchase an item.",
  "type": "object",
  "properties": {
    "shippingAddress": {
      "title": "Shipping Address",
      "description": "Buyer's address to ship the item to.",
      "type": "string"
    "email": {
      "title": "Email",
      "description": "Email of the buyer requesting to buy a Listing.",
      "type": "string",
      "pattern": "^\\S+@\\S+\\.\\S+$",
      "format": "email",
      "minLength": 6,
      "maxLength": 127
  "required": ["email", "shippingAddress"]

You could encrypt and then upload metadata like the following:

  "shippingAddress": "123 Main St",
  "email": ""


Offers to buy orders or sell orders have metadata. Before publishing a user's shipping address and other offer information, you should encrypt it with the encryptionPublicKey in the OrderMetadata.

To ensure a the other party can decrypt it, all offers have the form:

interface OfferMetadata {
  // The encrypted data, as a hex-encoded JSON string.
  message: string;

  // A random string included in the message, as a hex-encoded string.
  // Used to ensure the same string does not encrypt to the same message
  nonce: string;

  // The public key of the offerer that was used to encrypt the message.
  encryptionPublicKey: string;

  // A message that, if signed by the user, can be used to regenerate the
  // private key and unlock the message for the offerer.
  scope?: string;

Generating an encryption key

RWTP client applications frequently need access to encrypted information and lots of it. However, it is not a very bad idea to give an application your ethereum private key.

Therefore, we recommend you have a user sign a message, and using the sha256(sig) as the private key for the purposes of encryption.

For example:

import { signatureToPrivateKey } from 'rwtp';

const scope =
  'Do you want to allow this software to access your shipping information?';

const sig = await signer.signMessage('scope');
const key = signatureToPrivateKey(sig);

The scope is the literal message that you have the user sign. Store the scope in the metadata so you can regenerate the encryption again. If you don't store the scope, it may be hard to change the human-readable message.

1 You're free to use other data storage solutions, like Arweave. However, the creators of Real World Trade Protocol pin and archive IPFS CIDs under a certain size limit to help secure the network.