NAV Navbar
javascript
  • Introduction
  • Platform Components
  • Architecture
  • Getting started
  • Local Development
  • Authentication & Identity
  • Origin Object
  • Marketplace
  • Attestation
  • Review
  • User
  • Protocol Schemas
  • Getting help
  • Contributing
  • Introduction

    Origin provides a simple and powerful javascript library for developers to build decentralized marketplaces, allowing buyers and sellers to meet and transact without requiring any trusted intermediaries.

    Usage

    Visit our Github: https://github.com/OriginProtocol

    This API documentation will explain how developers can use the origin.js library to create and manage decentralized marketplaces that are built on top of IPFS and the Ethereum network.

    Origin.js aims to create an easy and flexible abstraction layer that:

    Origin.js enables developers to create DApps that onboard new users to the Origin platform, add new listings to the listings registry, create booking contracts, close out bookings (transfer funds, write reviews, etc.), and more.

    Notes

    Please note this project is still in heavy development and many of the features described below have not been implemented yet. This library should not be considered as production-ready.

    Origin.js and the entire Origin Protocol project is 100% open-source and we welcome contributions from the community. There are many ways to help, from reporting issues and contributing code to helping us improve and grow our community.

    If you are interested in getting involved, please read our section on contributing. If at any point you get stuck, please reach out and we'll do our best to help.

    Platform Components

    Origin.js provides the interface for developers to interact with the rest of the Origin platform, all without writing Solidity code or managing IPFS instances.

    At a high-level, the Origin platform consists of user, listing, and booking data and logic stored on the decentralized tech stack. Mission-critical data and logic such as booking availability, transaction history, and escrow rules are generally stored on chain in a series of Ethereum smart contracts. Related metadata such as listing descriptions and images are stored on IPFS, with pointers to this data in the form of content hashes being stored on chain.

    User Registry

    The Origin user registry is a datastore of all Origin-enabled users. Origin users are identified by their Ethereum wallet addresses. In addition, the user registry also stores a mapping of all forms of identity verification that the user has successfully undertaken.

    Origin.js enables developers to register users to the shared user registry, as well as query for identity verifications.

    Listing Registry

    The listing registry stores all valid Origin listings, from cars for rent to freelance design services. Developers will be able to create new listings in JSON, then push them to the Origin listing registry. Under the covers, Origin.js handles the creation of new IPFS content files for static metadata and new entries to the Origin listing registry smart contract.

    Note that Origin.js does not support browsing and searching the listing registry directly. It is recommended that developers use our open-source bridge server to efficiently query the blockchain.

    Booking Contracts

    Booking contracts are automatically created when buyers book listings on Origin-powered DApps. These individual smart contract instances are generated and deployed with rules around price, reservation time, and payment rules. For certain listing types, Origin.js will also generate additional contract code for arbitration, escrow, deposits, payment schedules, etc.

    Contract Modularity

    Origin smart contracts are designed to be flexible and modular. We recognize the need for developers and entrepreneurs to have a choice in selecting smart contract components that tailor-serve their needs.

    To that end, we will provide default contracts for escrow, arbitration, and insurance that will be inherited by our booking smart contracts. However, developers will be able to specify alternative contracts of their choosing (either their own or approved third-party contracts) in Origin.js function calls to generate custom booking contracts.

    Architecture

    If you're new to the space, it may be helpful to first familiarize yourself with some of the core technologies that we're using to build Origin, such as JSONSchema, IPFS and Ethereum.

    Origin listings can be created using a frontend DApp to publish a JSON data object to any publicly writeable IPFS gateway. This JSON data object must conform to a set of standards and validation rules to be considered valid on the network. Users can optionally sign their listings cryptographically to verify their identity using publicly auditable proofs or trusted third parties. The IPFS gateway will publish the listing to the IPFS network making the listing instantly available via hundreds of distributed computers around the world to anyone who knows the content hash. The content hash of the listing is then sent to a smart contract which formally publishes the listing and stores pricing and availability information along with any specified booking rules and policies.

    Listings can easily be searched, browsed, and booked via a frontend DApp. Since we anticipate having too many listings to reasonably parse in a browser, the frontend DApp connects to an open-source bridge server of the user’s choosing, making it possible to search and filter the entire public corpus of listings. Once a listing has been selected, a user can make a booking by sending payment to the booking smart contract along with the IPFS hash of the chosen listing and the desired interval to book. The smart contract will verify that the booking is valid and handle the transfer of tokens between the buyer and the seller, including the escrow of funds when applicable.

    We anticipate most sellers will prefer to list their prices in fiat currencies which often have less volatility than digital currencies. To solve this challenge, both the booking smart contract and the bridge servers will use a common set of oracles and a shared algorithm to determine the exchange rate to be used. This allows prices to be shown to end users in their preferred fiat currencies while the correct amount of digital tokens are sent during the booking. A diverse set of oracles will be chosen to avoid introducing single points of failure into the system.

    Sellers are responsible for disclosing their preferred messaging channels in their listings through which buyers can contact them before, during, or after a transaction. Buyers can similarly indicate their preferred messaging channels when they complete a booking. Non-transactional communication between buyers and sellers will occur off-chain, and both parties are encouraged to only use secure and verifiable communication channels. For transactions that have a possibility of needing arbitration, a multisignature messaging channel should be chosen that includes the arbitrator in all communications.

    Once a transaction is complete, users are encouraged via economic incentives to leave feedback about the interaction in the form of a rating or review. Once again, the content is stored on IPFS and only the content hash is stored on Ethereum. Users are able to establish their reputations over time with verified transactions, building a unified reputation across multiple listing verticals. Buyers can use different wallets with varying levels of identity attached for sensitive transactions, or choose to only reveal their true identity to the seller while using a throw-away wallet.

    Listing policies around escrow, refunds, required deposits, and cancellations are set by the seller and are strictly enforced by the booking smart contract. Any exceptions to the policies must be handled directly off-chain by the two parties.

    JSONSchema

    Learn more about JSONSchema

    Ethereum

    Learn more about Ethereum

    IPFS

    Learn more about IPFS

    Getting started

    Download Origin

    Origin.js is under active development. Our latest releases are available on our Github.

    Use an Ethereum-enabled browser

    For testing and interacting with your DApp, you will need to use a browser that supports Web3. We recommend using the Metamask Chrome Browser Extension. This will enable you to connect to the Ethereum network from your browser. Metamask allows you to run Ethereum DApps right in your browser without running a full Ethereum node.

    Alternatively, you can run the official Ethereum browser Mist.

    On mobile, we recommend trying Toshi, Cipher and Trust Wallet.

    Acquire Test ETH

    Our smart contracts are currently deployed on the Rinkeby testnet. You will need to have test ETH to use this library. You can request test funds from the Rinkeby faucet. Do not yet send real ETH on the Origin network. Use Rinkeby testnet ETH instead.

    Hello World

    Simply include the downloaded javascript library in your html to get started.

    Sample app

    <html>
    <title>Hello World</title>
    <body>
      <script type="text/javascript" src="origin.js"></script>
    </body>
    </html>
    
    const origin = new Origin();
    
    const listingData = {
      name: "Kettlebell For Sale",
      category: "Health and Beauty",
      location: "San Fransisco, CA",
      description:
        "32kg gorilla kettlebell. Mint condition.",
      pictures: [],
      price: 0.134
    }
    const schema = "for-sale"
    const transaction = await origin.listings.create(listingData, schema)
    await origin.contractService.waitTransactionFinished(transaction.tx)
    

    Local Development

    Ready to get started developing Origin.js and the Demo DApp? You will need two terminal windows: one for origin.js and one for the DApp.

    Origin.js:

        mkdir -p origin
        cd origin
        git clone https://github.com/OriginProtocol/origin.git
        cd origin/origin-js
        git checkout develop
        npm run install:dev
        npm run start
    

    And in another window, let's get the Demo DApp going:

        cd .. (or /path/to/origin)
        git clone https://github.com/OriginProtocol/origin.git
        cd origin/origin-dapp
        git checkout develop
        npm run install:dev
        npm run start
    

    Your local repos have been setup to use the develop branches, since that's where all our new work starts from. You have also linked the Demo DApp to use your local copy of the Origin.js library, so that your changes to Origin.js are reflected immediately.

    You will now need to have MetaMask installed in your browser and connect it to "Localhost 8545". Then visit http://localhost:3000/ to see the DApp.

    If you make changes to the contract code, or want to reset the contracts back to their starting states, you will want to stop (ctrl-c) your Origin.js development server. Then start it back up again with npm run start. After doing this, you must clear MetaMask's caches (or you will be getting old and invalid data). To do this:

    Authentication & Identity

    Identity with ERC 725

    Users identities are tied to Ethereum addresses and your private keys are used as the sole method of authentication on the Origin platform.

    Users can always prove ownership of a wallet without sending funds, simply by signing a message using their private keys.

    Users can assume multiple identities by creating multiple wallets. In this manner, users can choose how much of their off-line identities they wish to reveal to other users while participating on the Origin network.

    We are using the ERC 725 identity standard as proposed by Fabian Vogelsteller as the basis for identity on the Origin platform. We've built an identity playground to help people understand ERC 725 and how it works.

    The Origin run bridge server will provide identity attestations as a community service. Origin identities are portable not only across marketplaces built on our platform, but also with other DApps that are supporting the ERC 725 standard.

    Origin Object

    The Origin object is the base of interaction with origin.js. Other resources are members of this object. For example, origin.listings, origin.purchases, origin.attestations, etc.

    Configuration

    Configuration options are passed into the Origin constructor at instantiation.

    let configOptions = {
      option: 'value'
    }
    let origin = new Origin(configOptions)
    

    Valid options:

    For example, if you are running a local IPFS daemon then you could set following config options:

    
    const configOptions = {
      ipfsDomain: '127.0.0.1',
      ipfsApiPort: '5001',
      ipfsGatewayPort: '8080',
      ipfsGatewayProtocol: 'http'
    }
    const origin = new Origin(configOptions)
    

    Marketplace

    The marketplace manages listings from creation to sale as well as disputes between users.

    getListings

    This will return information about the listing, combining information from IPFS and the blockchain. In the future, fields returned may differ based on the listing's schema.

    A listing is a published action from a seller to sell something. It is active until there are no more units available or its expiration date is reached.

    Example: getListings

    
    > origin.marketplace.getListings({ idsOnly: true })
    
    //returns
    
    ['0-34-2-34', '000-234']
    
    > origin.marketplace.getListings({ listingsFor: '0x627306090abab3a6e1400e9345bc60c78a8bef57' })
    
    //returns all listings for '0x627306090abab3a6e1400e9345bc60c78a8bef57'
    
    [{
      id: "99-0023",
      title: "Kettlebell For Sale",
      media: [],
      schemaId: "09398482-2834",
      unitsTotal: 1,
      type: "unit",
      category: "Health and Beauty",
      subCategory: "daily exercise",
      language: "English",
      description: "32kg gorilla kettlebell",
      price: { currency: 'ETH', amount: '0.5' },
      commission: { currency: 'OGN', amount: '1' },
      ipfsHash: "QmWZDcDq4aYGx9XmkPcx4mnKaGW2jCxf5tknrCtbfpJJFf",
      seller: "0x627306090abab3a6e1400e9345bc60c78a8bef57",
      depositManager: '0x02394099fu9dfse0920394u329u4024',
      status: 'active',
      offers: [], //is this supposed to be an array or an object?
      expiry: '1529674159',
      events:[{ id: '20949-345', event: 'ListingCreated' }]
    }]
    

    Arguments:

    Name Type Required Description
    idsOnly boolean optional
    listingsFor string optional user's address
    purchasesFor string optional user's address

    createListing

    When you create a listing, the API will create both the IPFS data and the Listing contract on the blockchain. When a listing is successfully created, the createListing method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: createListing

    
    const listing = {
      category: "schema.forSale",
      commission: { amount: "0", currency: "OGN" },
      description: "ojwoifj weofijfoijfewoi qfoiqejqoidjq oidwjqdo",
      language: "en-US",
      listingType: "unit",
      media: [{}],
      price: { amount: "0.01", currency: "ETH" },
      schemaId: "http://schema.originprotocol.com/listing_v1.0.0",
      subCategory: "schema.forSale.appliances",
      title: "I am a great appliance listing",
      unitsTotal: 1
    }
    
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.createListing({ listing }, callback)
    

    Arguments:

    Name Type Required Description
    listing object required
    callback function optional provides args confirmationCount and transactionReceipt

    withdrawListing

    Withdrawing a transaction will set the unitsAvailable to zero. This will stop any further offers for that Listing. When a listing is successfully withdrawn, the withdrawListing method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: withdrawListing

    
    const listingId = "927-832"
    const data = {}
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.withdrawListing(listingId, data, callback)
    

    Arguments:

    Name Type Required Description
    listingId string required id of the listing to be withdrawn
    data object optional default to {} if not needed
    callback function optional provides args confirmationCount and transactionReceipt

    getListingReviews

    Reviews are created by the buyer in the receipt stage and by the seller in the payout stage of the offer process. A review consists of a required 1-5 rating and an optional reviewText text field.

    Example: withdrawListing

    
    const listingId = "927-832"
    
    > origin.marketplace.getListingReviews(listingId)
    
    //returns
    
    [{
      "schemaId": "http://schema.originprotocol.com/review_v1.0.0",
      "rating": 4,
      "text": "Solid Listing"
      "reviewer": "0x29884972398479234792"
    ]}
    

    Arguments:

    Name Type Required Description
    listingId string required id of the listing

    getNotifications

    Each Notification corresponds to the status of a Listing. Notifications are currently generated for each of the following Listing statuses:

    Notifications do not exist on the blockchain nor are they read from a database. They are derived from the blockchain transaction logs of the Listing statuses at the time of the API request. Because of this, there is no central record of a notification's status as "read" or "unread". When a client first interacts with the notifications API, Origin.js will record a timestamp in local storage. All notifications resulting from blockchain events that happen prior to this timestamp will be considered to be "read". This ensures that when the same user interacts with the notifications API from a different client for the first time, they will not receive a large number of "unread" notifications that they have previously read from their original client.

    Example: getNotifications

    
    > origin.marketplace.getNotifications()
    
    //returns all notifications for the user
    
    [{
      "id": "2984803-23433",
      "type": "buyer_listing_shipped",
      "status": "unread",
      "event": {},
      "resources": { listingId: "927-832", offerId: "183", listing: { title: "Whirlpool Microwave" } }
    ]}
    

    setNotification

    Since notification objects do not live on the blockchain or in a database, this method only records an update to the client's local storage. It accepts a single parameter, which should be an object containing the id of the notification and a status value of either read or unread. Any other properties included in this object will be ignored.

    Example: setNotification

    
    const id = "2984803-23433"
    const status = "read"
    
    > origin.marketplace.setNotification({ id, status })
    
    

    Arguments:

    Name Type Required Description
    id string required id of the notification
    status string required "read" or "unread"

    getOffer

    This will return a specific offer sent by a buyer for a listing offer.

    An Offer is a single transaction between a buyer and seller. A single Listing with multiple items for sale could have many Offers related to it, one for each buyer.

    A new Offer contract is created when a buyer purchases a Listing.

    Example: getOffer

    
    const offerId = "2403-234"
    
    > origin.marketplace.getOffer(offerId)
    
    //returns the specified offer
    
    {
      id: "2403-234",
      listingId: "999-000-0",
      status: "accepted",
      createdAt: 1539991086,
      buyer: "0x627306090274fwfiou97h0c78a8BEf57",
      events: [...],
      refund: "0",
      schemaId: "http://schema.originprotocol.com/offer_v1.0.0",
      listingType: "unit",
      unitsPurchased: 1,
      totalPrice: { currency: "ETH", amount: "0.033" },
      ipfs: {...}
    }
    

    Arguments:

    Name Type Required Description
    offerId string required offer.id

    getOffers

    This will return all offers related to a specific listing.

    Example: getOffers

    
    const listingId = "9903-75"
    const options = {
      for: "0x627306090274fwfiou97h0c78a8BEf57"
    }
    
    > origin.marketplace.getOffers(listingId, options)
    
    //returns all offers for the specified listing
    
    [{
      id: "999-000-0-0",
      listingId: "9903-75",
      status: "created",
      createdAt: 1539991086,
      buyer: "0x627306090274fwfiou97h0c78a8BEf57",
      events: [...],
      refund: "0",
      schemaId: "http://schema.originprotocol.com/offer_v1.0.0",
      listingType: "unit",
      unitsPurchased: 1,
      totalPrice: { currency: "ETH", amount: "0.033" },
      ipfs: {...}
    },
    {
      id: "82838-247-3-3",
      listingId: "9903-75",
      status: "created",
      buyer: "0x938828348ske02heo92394hwf",
      ...
    }]
    

    Arguments:

    Name Type Required Description
    listingId string required listing.id
    options object optional const options = { for: "0x627..." }

    makeOffer

    The makeOffer method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: makeOffer

    
    const listingId = "9903-75"
    const offer = {
      id: "82838-247-3-3",
      listingId: "9903-75",
      status: "created",
      buyer: "0x938828348ske02heo92394hwf",
      ...
    }
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.makeOffer(listingId, offer, callback)
    
    

    Arguments:

    Name Type Required Description
    listingId string required listing.id
    offer object required args see Protocol Schemas: Offer Schema
    callback function optional provides args confirmationCount and transactionReceipt

    acceptOffer

    The acceptOffer method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: acceptOffer

    
    const offerId = "543-0099"
    const data = {}
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.acceptOffer(offerId, data, callback)
    
    

    Arguments:

    Name Type Required Description
    offerId string required offer.id
    data object optional default to {} if not needed
    callback function optional provides args confirmationCount and transactionReceipt

    finalizeOffer

    The finalizeOffer method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: finalizeOffer

    
    const offerId = "9903-75"
    const buyerReview = {
      rating: 5
      schemaId: "http://schema.originprotocol.com/review_v1.0.0"
      text: "Great response times. Professional."
    }
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.finalizeOffer(offerId, buyerReview, callback)
    
    

    Arguments:

    Name Type Required Description
    offerId string required offer.id
    buyerReview object required args rating, schemaId, text
    callback function optional provides args confirmationCount and transactionReceipt

    withdrawOffer

    The withdrawOffer method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: withdrawOffer

    
    const offerId = "543-0099"
    const data = {}
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.withdrawOffer(offerId, data, callback)
    
    // returns the offer with a OfferWithdrawn event
    
    {
      offerId: "543-0099",
      events: { OfferWithdrawn: {...} }
      ...
    }
    

    Arguments:

    Name Type Required Description
    offerId string required offer.id
    data object optional default to {} if not needed
    callback function optional provides args confirmationCount and transactionReceipt

    addData

    The addData method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: addData

    
    const offerId = "543-0099"
    const listingId = "9900-234"
    const sellerReview = {
      rating: 4
      schemaId: "http://schema.originprotocol.com/review_v1.0.0"
      text: ""
    }
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.addData(listingId, offerId, sellerReview, callback)
    
    // returns a timestamp and transaction receipt
    
    {
      timestamp: 1540221215
      events: { OfferWithdrawn: {...} }
      ...
    }
    

    Arguments:

    Name Type Required Description
    listingId string optional can be null
    offerId string optional can be null
    sellerReview object optional default to {} if not needed
    callback function optional provides args confirmationCount and transactionReceipt

    initiateDispute

    The initiateDispute method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: initiateDispute

    
    const offerId = "543-0099"
    const data = {}
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.initiateDispute(offerId, data, callback)
    
    // returns a timestamp and transaction receipt
    
    {
      timestamp: 1540221215
      events: { OfferDisputed: {...} }
      ...
    }
    
    

    Arguments:

    Name Type Required Description
    offerId string required
    data object optional default to {} if not needed
    callback function optional provides args confirmationCount and transactionReceipt

    resolveDispute

    The resolveDispute method takes a callback with two arguments:

    confirmationCount - the number of block confirmations

    transactionReceipt - an object with a timestamp and information about the transaction and the corresponding block

    Example: resolveDispute

    
    const offerId = "543-0099"
    const data = {}
    const ruling = 1
    const refund = 33000000000000000
    const callback = (confirmationCount, transactionReceipt) => {
      //manage response
    }
    
    > origin.marketplace.resolveDispute(offerId, data, ruling, refund, callback)
    
    // returns a timestamp and transaction receipt
    
    {
      timestamp: 1540221215
      events: { OfferRuling: {...} }
      ...
    }
    
    

    Arguments:

    Name Type Required Description
    offerId string required
    data object required default to {} if not needed
    ruling number required 0 or 1
    refund object required price in wei
    callback function optional provides args confirmationCount and transactionReceipt

    Attestation

    An Attestation is a confirmation that some piece of identity information has been verified by a trusted 3rd party.

    Origin provides an attestation service that users can optionally use to add attestations to their profile.

    The following attestations are currently offered:

    Currently, an attestation is simply a public confirmation that something has been verified by Origin. The information itself is not made public.

    For example, when a user adds an email attestation to their profile, all that anyone else will be able to see is that their email has been verified by Origin. The email address itself remains private.

    Note that these methods are used to simply generate attestation objects. Once an attestation object has been created, it needs to be added to a user object for it to take effect.

    phoneGenerateCode

    To send an SMS verification code

    await origin.attestations.phoneGenerateCode({
      countryCallingCode: "1",
      phone: "555-555-5555",
      method: "sms",
      locale: "en"
    })
    

    This will perform a phone verification by calling or sending a SMS to a phone number.

    The phone number that will receive the call or SMS is the countryCallingCode combined with the phone. The phone should therefore be provided in the national format.

    The method can either by "call" or "sms".

    The locale parameter is optional and it defines the language of the call or the SMS. If not provided a sensible default is used based on the country of the phone number. Supported languages are af, ar, ca, zh, zh-CN, zh-HK, hr, cs, da, nl, en, fi, fr, de, el, he, hi, hu, id, it, ja, ko, ms, nb, pl, pt-BR, pt, ro, ru, es, sv, tl, th, tr, vi.

    phoneVerify

    To verify a ownership of a phone number

    let phoneAttestation = await origin.attestations.phoneVerify({
      wallet: myWalletAddress,
      countryCallingCode: "1",
      phone: "555-555-5555",
      code: "123456"
    })
    // Returns (attestation object)
    {
      signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
      data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
      claimType: 10,
      service: "phone"
    }
    

    This will verify that the code submitted in the request is the one that was sent to the phone number in the phoneGenerateCode call. If it is valid, an attestation object will be returned.

    Note that the countryCallingCode and phone must have the same values as the values used in the phoneGenerateCode call.

    emailGenerateCode

    To send an email verification code

    await origin.attestations.emailGenerateCode({
      email: "me@my.domain"
    })
    

    This will send an email to the given email address containing a verification code.

    emailVerify

    To verify ownership of an email address

    let emailAttestation = await origin.attestations.emailVerify({
      wallet: myWalletAddress,
      email: "me@my.domain",
      code: "123456"
    })
    // Returns (attestation object)
    {
      signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
      data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
      claimType: 11,
      service: "email"
    }
    

    This will verify that the code submitted in the request is the one that was sent to the email address in the emailGenerateCode call. If it is valid, an attestation object will be returned.

    facebookAuthUrl

    To get Facebook authentication url

    let url = await origin.attestations.facebookAuthUrl()
    
    window.open(url, '', 'width=650,height=500')
    let code = await new Promise((resolve, reject) => {
      window.addEventListener('message', (e) => {
        if (String(e.data).match(/^origin-code:/)) {
          resolve(e.data.split(':')[1])
        }
      }, false)
    })
    console.log('code', code) // use this value in `facebookVerify`
    

    This will return a url which your dapp should open in a popup window. The page will ask the user to grant permissions to the Origin app, which will be used to verify their Facebook identity. Once permissions have been granted, the popup window will post a message back to the dapp. You should listen for this message, which will contain the code needed for the facebookVerify call.

    facebookVerify

    To verify ownership of a Facebook account

    let facebookAttestation = await origin.attestations.facebookVerify({
      code: "12345" // code obtained from `facebookAuthUrl`
    })
    // Returns (attestation object)
    {
      signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
      data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
      claimType: 3,
      service: "facebook"
    }
    

    This will perform Facebook oauth verification on the specified code. If it is valid, an attestation object will be returned.

    Note that code is the oauth code generated in facebookAuthUrl.

    twitterAuthUrl

    To get Twitter authentication url

    let url = await origin.attestations.twitterAuthUrl()
    
    window.open(url, '', 'width=650,height=500')
    let code = await new Promise((resolve, reject) => {
      window.addEventListener('message', (e) => {
        if (String(e.data).match(/^origin-code:/)) {
          resolve(e.data.split(':')[1])
        }
      }, false)
    })
    console.log('code', code) // use this value in `twitterVerify`
    

    This will return a url which your dapp should open in a popup window. The page will ask the user to grant permissions to the Origin app, which will be used to verify their Twitter identity. Once permissions have been granted, the popup window will post a message back to the dapp. You should listen for this message, which will contain the code needed for the twitterVerify call.

    twitterVerify

    To verify ownership of a Twitter account

    let twitterAttestation = await origin.attestations.twitterVerify({
      code: "12345" // code obtained from `twitterAuthUrl`
    })
    // Returns (attestation object)
    {
      signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
      data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
      claimType: 4,
      service: "twitter"
    }
    

    This will perform Twitter oauth verification on the specified code. If it is valid, an attestation object will be returned.

    Note that code is the code generated in twitterAuthUrl

    airbnbGenerateCode

    To generate verification code

    await origin.attestations.airbnbGenerateCode({
      wallet: myWalletAddress,
      airbnbUserId: // user's id on Airbnb website
    })
    // Returns (object)
    {
      code: "0x0deef6ef"
    }
    

    This will generate a unique code that should be inserted into user's Airbnb profile description.

    airbnbVerify

    To verify ownership of Airbnb profile

    let emailAttestation = await origin.attestations.airbnbVerify({
      wallet: myWalletAddress,
      airbnbUserId: // user's id on Airbnb website
    })
    // Returns (attestation object)
    {
      signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
      data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
      claimType: 5,
      service: "airbnb"
    }
    

    If user has inserted the correct code in his Airbnb profile, an attestation object will be returned.

    Review

    Reviews are created by the buyer in the receipt stage and by the seller in the payout stage of the purchase process. A review consists of a required 1-5 rating and an optional reviewText text field.

    find

    To lookup reviews

    const purchaseAddress = "0x521b8d5f9e432e6b23d79fac02e5792eb8746ce1"
    const reviews = await origin.reviews.find({purchaseAddress: purchaseAddress})
    // Returns 
    [{
        blockHash: "0xf8d740a4eb381f99c259129da68df30b6a63cb3596839b4b40f01e8e4b55821b",
        blockNumber: 20,
        ipfsHash: undefined,
        purchaseAddress: "0x521b8d5f9e432e6b23d79fac02e5792eb8746ce1",
        rating: 5,
        reviewText: "",
        revieweeAddress: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57",
        revieweeRole: "BUYER",
        reviewerAddress: "0x821aEa9a577a9b44299B9c15c88cf3087F3b5544",
        timestamp: 1526496009,
        transactionHash: "0x94642427369cc50cee75a92df128c654c7d62b644d6c00f76e2404eadadf38bb"
    },
    {
        rating: 5,
        reviewText: "Very quick payment",
        blockHash: "0xcce740a4eb381f99c259129da68df30b6a63cb3596839b4b40f01e8e4b55821b",
        blockNumber: 220,
        ipfsHash: "QmfXRgtSbrGggApvaFCa88ofeNQP79G18DpWaSW1Wya1u8",
        purchaseAddress: "0x521b8d5f9e432e6b23d79fac02e5792eb8746ce1",
        revieweeAddress: "0x821aEa9a577a9b44299B9c15c88cf3087F3b5544",
        revieweeRole: "SELLER",
        reviewerAddress: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57",
        timestamp: 1526496009,
        transactionHash: "0xAC642427369cc50cee75a92df128c654c7d62b644d6c00f76e2404eadadfr91a"
    }]
    

    You can find reviews with the following filter options:

    User

    A User is an object used to represent a particular user in Origin Protocol. Users are implemented as ERC725 identities. Identities in Origin will be portable across platforms that support ERC725.

    A user object consists of 4 properties:

    set

    To create/update a user

    // Get a phone attestation object
    await origin.attestations.phoneGenerateCode({
      phone: "555-555-5555"
    })
    let phoneAttestation = await origin.attestations.phoneVerify({
      phone: "555-555-5555",
      code: "123456"
    })
    
    // Get a Facebook attestation object
    let url = await origin.attestations.facebookAuthUrl()
    
    // Open facebook authentication popup and retrieve authentication code
    window.open(url, '', 'width=650,height=500')
    let code = await new Promise((resolve, reject) => {
      window.addEventListener('message', (e) => {
        if (String(e.data).match(/^origin-code:/)) {
          resolve(e.data.split(':')[1])
        }
      }, false)
    })
    
    // Send code to obtain attestation
    let facebookAttestation = await origin.attestations.facebookVerify({
      code: code
    })
    
    let myNewUser = {
      profile: { firstName: "Wonder", lastName: "Woman" },
      attestations: [ phoneAttestation, facebookAttestation ]
    }
    await origin.users.set(myNewUser)
    let createdUser = await origin.users.get()
    
    // User has been created!
    
    // Get an email attestation object
    await origin.attestations.emailGenerateCode({
      email: "me@my.domain"
    })
    let emailAttestation = await origin.attestations.emailVerify({
      email: "me@my.domain",
      code: "123456"
    })
    
    createdUser.attestations.push(emailAttestation)
    await origin.users.set(createdUser)
    let updatedUser = await origin.users.get()
    
    // User has been updated!
    // final `updatedUser`:
    {
      {
        address: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57",
        profile: { firstName: "Wonder", lastName: "Woman" },
        attestations: [
          {
            signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
            data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
            claimType: 10,
            service: "phone"
          },
          {
            signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
            data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
            claimType: 3,
            service: "facebook"
          },
          {
            signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
            data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
            claimType: 4,
            service: "twitter"
          },
          {
            signature: "0xeb6123e537e17e2c67b67bbc0b93e6b25ea9eae276c4c2ab353bd7e853ebad2446cc7e91327f3737559d7a9a90fc88529a6b72b770a612f808ab0ba57a46866e1c",
            data: "0x7f5e752d19fee44e13bb0cc820255017c35584caddc055641d6ccadfa3afca01",
            claimType: 5,
            service: "airbnb"
          }
        ],
        identityAddress: "0x4E72770760c011647D4873f60A3CF6cDeA896CD8"
      }
    }
    

    If the user does not already exist, it will be created. If it exists, it will be updated.

    Note: this method should be used as if it will completely override the existing data. Updates should be made by taking the existing user object, making modifications to it, and then passing the entire updated object into the set method.

    get

    To retrieve a user

    let myUser = await origin.users.get()
    let anotherUser = await origin.users.get(otherUserAddress)
    // Returns (user object)
    {
      address: '',
      profile: {
        firstName: '',
        lastName: '',
        description: '',
        avatar: ''
      },
      attestations: [],
      identityAddress: ''
    }
    

    With no parameters passed in, this will return the current user. If a wallet address is passed in, this will return the user associated with that wallet.

    Protocol Schemas

    Listing Schema

    This describes a listing for purchase on the Origin marketplace.

    {
        schemaId: "http://schema.originprotocol.com/listing_v1.0.0",
        listingType: "unit",
        category: "schema.forSale",
        subCategory: "schema.forSale.carsTrucks",
        language: "en-us",
        title: "1997 Lotus Esprit",
        description: "Red. 100K miles. Always garaged.",
        expiry: "2018-09-20T19:59:36.875Z",
        media: [
            {url: "ipfs://Ac92fa25B89D37077C7e52BDae22094f7b864e91"},
            {url: "ipfs://C9a2fa25B89D37045C7e52BDae22094f7b864b82"},
        ],
        price: {amount: "48.7", currency: "ETH"},
        unitsTotal: 1,
        commission: {amount: "500", currency: "OGN"},
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/listing_v1.0.0
    listingType Must be set to unit
    category A category/schema key string from the DApp. See possible values here
    subCategory A subcategory key string from a schema in the DApp. See possible values inside each DApp schema here
    language A single language or locale tag that the listing text is written in.
    title The short title of the listing
    description A full textual description of the listing. Linebreaks are currently ignored, though this will change.
    price For currencies, we support ETH, with future plans to support any ERC20 token
    unitsTotal The number of units available for sale. Currently limited to 1

    Optional Fields

    expiry A date that this listing should no longer show in search results. This is suggestion and search engines may ignore it.
    media A list of media elements to be shown with the listing. We currently only support images on the ipfs:// protocol.
    commission This is the amount the seller is willing to pay the DApp that sells the listing user after the sale is complete. The currency must be OGN. This money comes out of the deposit amount the seller puts into the listing. Note that the deposit amount may very well be too low to actualy pay the commission - this must be checked for before trusting the commission.

    Offer Schema

    An Offer is sent by a buyer to make a purchase from a listing.

    {
        schemaId: "http://schema.originprotocol.com/offer_v1.0.0",
        listingType: "unit",
        unitsPurchased: 1,
        totalPrice: {amount: "48.7", currency: "ETH"},
        commission: {amount: "500", currency: "OGN"}
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/offer_v1.0.0
    listingType Must be set to unit
    unitsPurchased The number of units to purchase. Currently must be 1.
    totalPrice The price the buyer is willing to pay. For currencies, we support ETH, with future plans to support any ERC20 token.
    commission UNSAFE FIELD - this data duplicates, and may not match, the actual blockchain contract amount. The amount the buyer's DApp will receive for the sale. Must be equal to, or less than, the listing's commission amount. The currency must be OGN. The amount may be zero if there is no listing commission set.

    Dispute Schema

    A dispute can be started by either party. In v1.0.0, this schema contains no useful required fields, and is placeholder for future expansion. It is intended that the parties and the arbitrator will communicate over Origin Messenger after the dispute process is started.

    {
        schemaId: "http://schema.originprotocol.com/dispute_v1.0.0",
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/dispute_v1.0.0

    Listing-Withdraw Schema

    In v1.0.0, this schema contains no useful required fields, and is placeholder for future expansion.

    {
        schemaId: "http://schema.originprotocol.com/listing-withdraw_v1.0.0",
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/listing-withdraw_v1.0.0

    Offer-Accept Schema

    In v1.0.0, this schema contains no useful required fields, and is placeholder for future expansion.

    {
        schemaId: "http://schema.originprotocol.com/offer-accept_v1.0.0",
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/offer-accept_v1.0.0

    Offer-Withdraw Schema

    In v1.0.0, this schema contains no useful required fields, and is placeholder for future expansion.

    {
        schemaId: "http://schema.originprotocol.com/offer-withdraw_v1.0.0",
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/offer-withdraw_v1.0.0

    Profile Schema

    A profile allows a user to let the world know a little more about them.

    {
        schemaId: "http://schema.originprotocol.com/profile_v1.0.0",
        firstName: "Jake",
        lastName: "Lewis",
        avatar: "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",
        description: "Marketing Executive in Belfast."
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/profile_v1.0.0
    firstName The user's first name.
    lastName The user's last name.

    Optional Fields:

    avatar A data url image for the user's profile image.
    description A textual description by the user about themselves.

    Resolution Schema

    In v1.0.0, this schema contains no useful required fields, and is placeholder for future expansion.

    {
        schemaId: "http://schema.originprotocol.com/resolution_v1.0.0",
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/resolution_v1.0.0

    Review Schema

    A review of a single purchase by either a seller or a buyer.

    {
        schemaId: "http://schema.originprotocol.com/review_v1.0.0",
        rating: 5,
        text: "Buyer handled the notary process very quickly."
    }
    

    Required Fields:

    schemaId Must be set to http://schema.originprotocol.com/review_v1.0.0
    rating A number between 1 and 5. May be either an iteger or a decimal.
    text A textual review. May be a blank string - in which case it should not be shown by UIs.

    Getting help

    Discord

    We work in public and our company Discord is open to all. If you have questions or need help getting started, our Discord #engineering channel is a great place to get assistance from our team of engineers and developers.

    Email

    You can also reach us by email at support@originprotocol.com.

    Contributing

    Want to hack on Origin? Awesome!

    Origin is an Open Source project and we welcome contributions of all sorts. There are many ways to help, from reporting issues, contributing code, and helping us improve our community.

    There are three main repositories that are considered core components of the Origin platform:

    We also actively work on these repositories:

    Weekly Engineering Call

    We have an open weekly engineering call on Google Hangouts every Wednesday at 1pm pacific time. Add to Calendar

    Join the call here: https://meet.google.com/pws-cgyd-tqp

    Everyone is welcome to join, even if you're just lurking. All we ask is that you mute when not speaking to minimize background noise. All our meeting notes are public and can be reviewed here:

    Dive Right In

    If you're ready to start hacking on Origin right now and you just need an issue to focus on, check out our open issues especially those tagged as a good first issue.

    We use two week development sprints and you can see what we're currently prioritizing on our Github project board.

    Read our development process and community guidelines first and have fun!

    Development Process

    Getting your local environment running is easy with Origin Box. We recommend using this Docker container for any development or testing that involves running a local blockchain.

    Our branching strategy is similar to GitFlow, but we do all of our development in the master branch and have a stable branch for code that has been released.

    origin git branching model

    Your development flow should look like:

    1. Find an interesting issue and communicate! Please let the #engineering Discord channel know what you want to work on.
    2. Ping a core team member member on Discord and ask to be added to our contributors team. Otherwise, you'll need to fork the relevant repository and push feature branches to your own fork.
    3. Add a comment to the issue or self-assign so we don't have multiple contributors unintentionally working on the same task.
    4. Start with the master branch and check out a new feature branch unless you're contributing to an existing feature.
    5. Follow the appropriate coding style and write some awesome code.
    6. Pull the latest commits from master and confirm that your code works with any other work that has been merged since you started.
    7. Push your branch to the upstream repository (i.e. https://github.com/OriginProtocol/[repo]) so that other contributors can easily work off of it if necessary.
    8. Please request a review in the PR by clicking on the gear icon next to "Reviewers" in the right column.

    Both the master and stable branches are locked so that only members of the core team are able to merge your pull requests. Pull requests that are peer reviewed by other trusted contributors will be fast-tracked and merged faster! Check in the #engineering Discord channel for appropriate reviewers.

    Coding Style

    We use a variety of programming languages in our repositories. When contributing, please follow existing coding conventions and refer to the CONTRIBUTING.md file in the repository, if one exists.

    For JavaScript, we use NPM's style, which is automatically enforced via prettier.

    For Solidity, we use two space indents.

    Troubleshooting a Development Environment

    Protocol Design

    When considering protocol or implementation design proposals, we are looking for:

    Please note that protocol design is hard and meticulous work. You may need to review existing literature and think through generalized use cases.

    Community Guidelines

    We want to keep the Origin community awesome, growing and collaborative. We need your help to keep it that way. To help with this we've come up with some general guidelines for the community as a whole:

    Reporting Issues

    If you find bugs, mistakes or inconsistencies in the Origin project's code or documents, please let us know by filing an issue at the appropriate issue tracker (we use multiple repositories).

    Security Issues

    The Origin Protocol and its implementations are still in early development, which means there may be problems with the protocol or in our implementations. We take security vulnerabilities very seriously. If you discover a security issue, please bring it to our attention right away!

    If you find a vulnerability please send your report privately to security@originprotocol.com or contact Josh Fraser via Keybase. Please DO NOT file a public issue.

    If the issue is a protocol weakness or something not yet deployed, feel free to discuss it openly.

    Community Improvement

    Origin is just as much about community as it is about our technology.

    We need constant help in improving our documentation, building new tools to interface with our platform, spreading the word to new users, helping new users getting setup and much more.

    Please get in touch if you would like to help out. Our general channel on Discord is a great place to share ideas and volunteer to help.

    Full Time Positions

    Origin occasionally hires developers for part time or full time positions.

    We have a strong preference for hiring people who have already started contributing to the project. If you want a full time position on our team, your best shot is to engage with our team and start contributing code. It is very unlikely that we would offer you a full time position on our engineering team unless you've had at least a few pull requests merged.

    If you are interested, check out the Origin Protocol job listings. If you'd like to help in other ways, please propose your ideas in the Origin Discord.