Package: Polymath Offchain - PolymathNetwork/polymath-apps GitHub Wiki
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
The service makes use of the following models:
This model represents a pairing between an Ethereum address and a randomly generated hex string (Verification Code) for authentication purposes via signing.
- code:
string
randomly generated hex string - address:
string
issuer's Ethereum address
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.
- 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
This model represents an important piece of information that Polymath wants to deliver to the users of the dApps.
- type:
string
type of notice (error
|warning
|info
) - scope:
string
users that should receive this notice (issuers
|investors
|all
) - 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
This model represents a service provider in the Polymath ecosystem.
- 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
This model represents a user account
- 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
The service implements the following submodules:
This module handles authentication signing validation. It follows the standard set by EIP712.
/**
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.
*/
This module handles all the emails that the offchain service sends. It exports one function per email type.
/**
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
*/
/**
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
*/
/**
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
*/
/**
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
*/
/**
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
*/
The service exposes a variety of endpoints for the dApps to communicate with it
The following endpoints handle authentication and signing
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
}
}
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
}
The following endpoints handle email verification
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'
}
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',
}
The following endpoints manage issuers applying for a provider's services
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',
}
The following endpoints handle notices displayed in the dApps
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
}
}
The service implements the following react components to render the emails it sends.
Email header and footer that wraps every other email component.
Email that is sent to an issuer for email verification.
Props:
- pin:
string
randomly generated PIN code for verification in the dApp
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
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
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
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
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)
- 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