Package: Polymath Offchain - PolymathNetwork/polymath-apps GitHub Wiki

Package's Concerns

This package implements a NodeJS service that handles operations and data stored outside the blockchain.

Completed features:

  • Handles authentication by generating random codes which the user has to sign
  • Pairs the user's email address to his ethereum address via email confirmation
  • Sends emails to providers in the Polymath ecosystem when issuers apply for their services
  • Sends emails to issuers when certain key transactions regarding their Security Token are completed

Works in progress:

  • Create notices to send to the dApps regarding updates and relevant information
  • Create new Polymath ecosystem service providers in the database

Modules

Models

The service makes use of the following models:

AuthCode

This model represents a pairing between an Ethereum address and a randomly generated hex string (Verification Code) for authentication purposes via signing.

Schema

  • code: string randomly generated hex string
  • address: string issuer's Ethereum address

EmailPIN

This model represents the status of an email confirmation. It is used to check if an issuer entered the correct PIN (sent to his email) in order to associate his email to his Ethereum address.

Schema

  • pin: string randomly generated hex string
  • email: string issuer's email
  • address: string issuer's Ethereum address
  • isConfirmed: boolean whether the issuer already confirmed his email
  • createdAt: Date date of creation
  • updatedAt: Date date of last update

Notice

This model represents an important piece of information that Polymath wants to deliver to the users of the dApps.

Schema

  • type: string type of notice (error | warning | info)
  • scope: string users that should receive this notice (issuers | investorsall)
  • title: string notice title
  • content: string: notice description
  • isOneTime: boolean: whether the notice should appear only once or every time a user logs in
  • isValid: boolean: whether the notice should still be shown

Provider

This model represents a service provider in the Polymath ecosystem.

Schema

  • id: number provider identifier
  • email: string provider's email address
  • name: string provider's company name
  • createdAt: Date date of creation
  • updatedAt: Date date of last update

User

This model represents a user account

Schema

  • address: string user's Ethereum address
  • email: string user's email address
  • name: string user's name
  • createdAt: Date date of creation
  • updatedAt: Date date of last update

Utils

The service implements the following submodules:

Sig

This module handles authentication signing validation. It follows the standard set by EIP712.

Exported functions

async verifySignature
/**
  Check if a client trying to authenticate has signed the verification code and if the signature is valid

  @param {string} code polymath verification code
  @param {string} sig signature
  @param {string} address issuer Ethereum address

  @returns {Promise} promise that resolves to null if the signature is valid, or an object describing the error if it is not.
 */

Emails

This module handles all the emails that the offchain service sends. It exports one function per email type.

Exported functions

async sendProviderApplicationEmail
/**
  Sends an email to a provider notifying that an issuer has filled out an application form

  The application form has the following data:

  - companyName: the issuer's company name
  - companyDesc: description of the issuer's company
  - operatedIn: company's jurisdiction of operation
  - incorporatedIn: company's jursidiction of incorporation
  - projectURL: corporate/project presentation URL
  - profilesURL: management and board member profiles URL
  - structureURL: corporate or project structure/organization URL
  - otherDetails: other details about the company

  @param {string} providerEmail email address of the provider
  @param {string} providerName company name of the provider
  @param {string} issuerName name of the issuer
  @param {string} issuerEmail email address of the issuer
  @param {Object} application application form fields
  @param {boolean} dummy true if email is for testing purposes
 */
async sendTickerReservedEmail
/**
  Sends an email to the issuer notifying him of a ticker being reserved

  @param {string} issuerEmail email address of the issuer
  @param {string} issuerName name of the issuer
  @param {string} txHash transaction hash
  @param {string} expiryLimit number of days before the ticker reservation expires
  @param {string} networkId id of the network in which the ticker was reserved
 */
async sendTokenCreatedEmail
/**
  Sends an email to the issuer notifying him that his token has been created

  @param {string} issuerEmail email address of the issuer
  @param {string} issuerName name of the issuer
  @param {string} txHash transaction hash
  @param {string} ticker name of the token
  @param {string} networkId id of the network to which the token was deployed
 */
async sendSTOScheduledEmail
/**
  Sends an email to the issuer notifying him that an STO for his token has been scheduled

  @param {string} issuerEmail email address of the issuer
  @param {string} issuerName name of the issuer
  @param {string} txHash transaction hash
  @param {string} ticker name of the issuer's security token
  @param {number} cap maximum number of tokens that can be sold
  @param {string} fundsReceiver wallet address that investment funds will be transferred to
  @param {boolean} isPolyFundraise true if the funds are raised in POLY, false if they are raised in ETH
  @param {number} rate conversion rate between currency of choice and the issuer's security token
  @param {Date} start start date of the STO
  @param {string} networkId id of the network in which the STO was scheduled
 */
async sendAccountConfirmationEmail
/**
  Sends an email to the provided address in order to verify that it belongs to the issuer

  @param {string} issuerEmail address to send the confirmation email to
  @param {string} issuerName name of the issuer
  @param {string} pin randomly generated code to verify the email address in the dApp
 */

Routes

The service exposes a variety of endpoints for the dApps to communicate with it

Authentication

The following endpoints handle authentication and signing

GET /verification-code/:address

Auth setup route. The client provides his Ethereum address as a route parameter. A random code is assigned to the address for signing and is returned to the client in a JSON along with the typed message he has to sign.

If the address is invalid, the response is

{
  status: 'error',
  data: 'Invalid request parameters'
}

Otherwise it is

{
  status: 'ok',
  data: {
    typedName: string, // typed message to sign
    code: string // random verification code to sign
  }
}

POST /auth

Auth route.

Request body:

{
  code: string, // Polymath verification code
  address: string, // issuer's Ethereum address
  sig: string // issuer's signature
}

If the request body is invalid, the response is

{
  status: 'error',
  data: 'Invalid request body'
}

If the client didn't sign the verification code or the signature is not valid, the response will contain an error.

If the code doesn't match the address in our database, the response is

{
  status: 'error',
  data: 'Code is not valid'    
}

If the signature is invalid, the response is

{
  status: 'error',
  data: 'Sig is not valid'
}

If the client previously confirmed his email via PIN, the response will contain the client's email and name, signaling the dApps to allow access.

{
  status: 'ok',
  data: {
    name: string, // name of the user
    email: string // email address of the user
  }
}

Otherwise, the response data will be null, signaling that the email confirmation process has to begin before the client can be granted access.

{
  status: 'ok',
  data: null
}

Emails

The following endpoints handle email verification

POST /email/new

Email confirmation setup route. Receives an email address and sends a random confirmation PIN string to that address which is then requested by the dApp. Also stores (or updates) the user in the database.

Request body:

{
  email: string, // issuer's email address
  name: string, // issuer's name
  code: string, // Polymath verification code
  address: string, // issuer's Ethereum address
  sig: string // issuer's signature
}

If the request body is invalid, the response is

{
  status: 'error',
  data: 'Invalid request body'
}

If the client didn't sign the verification code or the signature is not valid, the response will contain an error.

If the code doesn't match the address in our database, the response is

{
  status: 'error',
  data: 'Code is not valid'    
}

If the signature is invalid, the response is

{
  status: 'error',
  data: 'Sig is not valid'
}

Otherwise, the response is

{
  status: 'ok',
  data: 'Confirmation email has been sent'
}

POST /email/confirm

Email confirmation route. Receives a PIN string from the client and validates if that PIN was created in the last 24 hours.

Request body:

{
  pin: string // PIN string for verification
}

If the request body is invalid, the response is

{
  status: 'error',
  data: 'Invalid request body'
}

If the PIN doesn't match a user in the database, the response is

{
  status: 'error',
  data: 'Pin is not valid',
}

Otherwise, the user's email is confirmed and the response is

{
  status: 'ok',
  data: 'Email has been confirmed',
}

Providers

The following endpoints manage issuers applying for a provider's services

POST /providers/apply

Provider application route. Receives form data and an array of the ids of the providers to send email applications to. It checks if the client has a reserved ticker and sends the corresponding emails.

Request body:

{
  code: string, // Polymath verification code
  sig: string, // issuer's signature
  address: string, // issuer's Ethereum address
  companyName: string, // issuer's company name
  ids: Array<number>, // provider ids
  companyDesc: string, // issuer's company description
  operatedIn: string, // company's jurisdiction of operation
  incorporatedIn: string, // company's jursidiction of incorporation
  projectURL: string, // corporate/project presentation URL
  profilesURL: string, // management and board member profiles URL
  structureURL: string, // corporate or project structure/organization URL
  otherDetails: string, // other details about the company
  networkId: string, // id of the network the client is connected to
}

If the request body is invalid, the response is

{
  status: 'error',
  data: 'Invalid request body'
}

If the client didn't sign the verification code or the signature is not valid, the response will contain an error.

If the code doesn't match the address in our database, the response is

{
  status: 'error',
  data: 'Code is not valid'    
}

If the signature is invalid, the response is

{
  status: 'error',
  data: 'Sig is not valid'
}

If there is no user with that Ethereum address in our database, the response is

{
  status: 'error',
  data: 'Invalid user'
}

If the network id is not valid (or offchain isn't connected to that particular network), the response is

{
  status: 'error',
  data: 'Invalid network id'
}

If the issuer has no reserved tickers, the response is

{
  status 'error',
  data: 'No ticker was reserved'
}

Otherwise, the response is

{
  status: 'ok',
  data: 'Application has been sent',
}

Notices

The following endpoints handle notices displayed in the dApps

GET /notice/:scope

Latest notice route. Given a scope string (issuer | investor), the route responds with the latest notice in that scope. Notices with 'all' scope are returned even if they are not requested.

If the scope is invalid, the response is

{
  status: 'error',
  data: 'Invalid request parameters'
}

If there is no valid notice in the database, the response is

{
  status: 'ok',
  data: undefined
}

Otherwise, the response is

{
  status: 'ok',
  data: {
    type: string, // notice type (error, warning, info)
    scope: string, // notice scope (all, issuers, investors)
    title: string, // notice title
    content: string, // notice content
    isOneTime: boolean, // whether the notice should only be shown once to the client
    isValid: boolean, // whether to show the notice
    createdAt: Date, // when the notice was created
    updatedAt: Date // when the notice was last updated
  }
}

Email Components (React)

The service implements the following react components to render the emails it sends.

EmailWrapper

Email header and footer that wraps every other email component.

AccountConfirmation

Email that is sent to an issuer for email verification.

Props:

  • pin: string randomly generated PIN code for verification in the dApp

ProviderApplication

Email that is sent to a provider when an issuer fills out an application form to request said provider's services.

Props:

  • issuerName: string name of the issuer
  • issuerEmail: string email address of the issuer
  • application: Object form data

STOScheduled

Email that is sent to an issuer when an STO is scheduled.

Props:

  • ticker: string security token symbol
  • start: Date time when the STO will begin
  • cap: number: maximum amount of tokens that can be sold
  • rate: number: conversion rate between currency of choice and the issuer's security token
  • isPolyFundraise: boolean true if the funds are raised in POLY, false if they are raised in ETH
  • fundsReceiver: string wallet address that investment funds will be transferred to
  • txHash: string transaction hash
  • networkId: string id of the network in which the transaction was made

TickerReserved

Email that is sent to an issuer when its ticker is reserved

Props:

  • txHash: string transaction hash
  • ticker: string reserved ticker
  • expiryLimit: number number of days before the ticker reservation expires
  • networkId: string id of the network in which the transaction was made

TokenCreated

Email that is sent to an issuer when its security token has been created

Props:

  • ticker: string token symbol
  • txHash: string transaction hash
  • networkId: string id of the network in which the transaction was made

Listeners

This service adds listeners to all relevant blockchain events in order to send the corresponding transaction emails. The following events are being listened to (ContractName.EventName):

  • SecurityTokenRegistry.RegisterTicker: Triggers when a ticker has been reserved
  • SecurityTokenRegistry.NewSecurityToken: Triggers when a security token has been created
  • SecurityToken.ModuleAdded: Triggers when a module is added to a security token (right now we only care about the CappedSTO module to see when an STO has been scheduled for a token)

TO-DOS:

  • Implement an endpoint to create new providers
  • Deprecate use of 'id' in the provider schema (use mongo ids instead)
  • Implement an endpoint to create new notices
  • Use polymath-js for interacting with the blockchain (waiting until it is rewritten)
  • Make AuthCode and EmailPIN documents expire after some time
  • Rewrite sig utility (it appears to have unnecessary code)
  • Refactor and improve the email components
  • Move all blockchain event listeners to a middleware app that communicates with offchain via API
⚠️ **GitHub.com Fallback** ⚠️