/smart-securities-standard

Splash Securities Standard - S3

Primary LanguageTypeScriptMIT LicenseMIT

version solidity status
0.3.0
0.4.24
experimental

Splash Securities Standard - S3

Overview

Splash Securities Standard - S3 has grown out of the Splash Exchange's efforts to automate certain aspects of running a compliant alternatives exchange. Currently, it consists of a smart contract library and a typescript library for manipulating these contracts. However, the full scope includes a standard protocol that exchanges an compliance providers can use to communicate in order to maintain the trading invariants required by the SEC for securities to keep their filing exemptions.

Contributing

If you would like to contribute, please see contributing.md before you begin. Then, take a look at the setup instructions below.

Architecture

Architecture diagram

The simplified S3 architecture provides a permissioned token with no rule checking on chain. There are three classes of contracts.

  • CapTables: All securities issued on S3 share this contract, which is only responsible for being the single source of truth for cap tables.
  • TokenFront: This contract provides a fixed Ethereum address for a given security. All calls are forwarded to a contract expressing rule logic.
  • SimpliedLogic: This contract implements a two stage clearing and settlement protocol. Users create token transfer requests by calling transfer and transferFrom on the associated TokenFront. Then a third party resolves each transfer request by providing an error code. Only on error code 0 is the transfer settled.

How to use the contracts

Start by having a look at src/Types.ts. Here is a simple example of programmatic online issuing:

import * as s3 from "@openfinance/smart-securities-standard";
import { readFileSync, writeFileSync } from "fs";
import * as Web3 from "web3";

const capTablesAddress = readFileSync("soon-to-be-deployed-s3-capTables.address", "utf8");
const security: s3.BaseSecurity = JSON.parse(readFileSync("mySecurity.json", "utf8"));

const prov = new Web3.providers.HttpProvider("http://localhost:8545");
const web3 = new Web3(prov);

async function go() {
  const record = await s3.issue(
    security, 
    capTablesAddress, 
    deploymentAddress, 
    Web3.eth
  );
  writeFileSync("my-deployment-record.json", JSON.stringify(record), "utf8");
}

go();

The command line tool

S3 ships with a command line tool you can access at npm run cli -- --help. To use the tool, create two files: config.json and spec.json and any number of secrurity declarations. See here for the appropriate format for these files, where the BigNumber fields can be either string or numbers in the json declaration files.

Summary of commands:

  • init: Deploy an instance of CapTables (this is safe to do online)
  • issueOnline: This is a convenience function for making sure that your deployment matches the declaration.
  • issueOffline: Use this function in an offline environment to generate transactions. It is safe to copy the generated transcripts to an online environment. This flow uses an ephemeral keypair to run the deployment.
    • issueOffline -s 1 (plus other flags as needed) will create a transcript file with the transaction data to perform the initial configuration of the CapTables contract, getting a new securityId. Note the controller address and base64-encoded key material generated by this command
    • publish -s 1 should be run online and will allow you to select a version of the transaction based on the gas price. Note: the controller address generated in the previous step must be funded before running this step.
    • issueOffline -s 2 will alter the transcript, adding transactions for distributing to investors and setting up the other parts of S3. Pass the base64-encoded key material from stage one to this script in the controller environment variable.
    • publish -s 2 will interactively broadcast the remaining transactions
  • publish: Run this online to broadcast transactions prepared offline

Issuing and maintaining

Manual issuance proceeds in several stages.

  • Stage I. Choose a deployed CapTables contract and send a transaction which calls initialize with your total supply. This will create a new security, owned by the caller and will give you the index of the security. The caller will hold the entire balance.
  • Stage II. Make calls to CapTables.transfer to configure the initial distribution of your security.
  • Stage III. Deploy SimplifiedLogic to address logicAddress, then deploy TokenFront with construction parameter logicAddress. Call setFront on SimplifiedLogic with the address of the TokenFront to authorize it to call in.
  • Stage IV. Make some provision to detect and resolve transfer requests. SimplifiedLogic will log TransferRequest messages as users attempt to move tokens around.
  • Stage V. If you need to modify the logic that governs token transfers, use the migrate method of CapTables and TokenFront.

Implemented Regulations

An S3 token can be managed using the handleTransfers<A> function. To use this function, you must implement:

// A decider with the restriction logic.  It should return a pair [code, x]
// where code is the error code (success = 0) and x is a payload that is consumed
// by the finalizer.
declare decision: (tr: Transfer) => Promise<[number, A]>

// A finalizer, which is used e.g. to persist the resolution hash and error
// code to a database.
declare finalization: (txHash: string, extraData: A) => Promise<void>;

Setting up S3 for development

In order to develop S3, you'll need to have some programs installed:

  • solc
  • jq
  • typescript, which can be installed with npm install -g typescript (or through your system package manager)

Then S3 can be set up like this:

$ git clone https://github.com/OpenFinanceIO/smart-securities-standard.git
$ cd smart-securities-standard
$ git submodule init && git submodule update # get OpenZeppelin contracts
$ npm install
$ npm run compile
$ npm run cli -- --help # see cli options

The tests expect to find ganache-cli listening at localhost:8545. Once ganache-cli has been started:

$ # run the test cases
$ npm test -- -t 30000