├── .babelrc ├── .eslintignore ├── .gitignore ├── .solcover.js ├── .soliumignore ├── .soliumrc.json ├── .travis.yml ├── LICENSE ├── PIPs ├── PIPS │ ├── pip-1.md │ └── pip-1 │ │ └── process.png ├── README.md └── pip-X.md ├── Polymath.png ├── README.md ├── build └── contracts │ ├── Compliance.json │ ├── Customers.json │ ├── ICompliance.json │ ├── ICustomers.json │ ├── IERC20.json │ ├── IOfferingFactory.json │ ├── ISecurityToken.json │ ├── ISecurityTokenRegistrar.json │ ├── ITemplate.json │ ├── Migrations.json │ ├── NameSpaceRegistrar.json │ ├── SafeMath.json │ ├── SecurityToken.json │ ├── SecurityTokenMOCK.json │ ├── SecurityTokenRegistrar.json │ ├── SimpleCappedOffering.json │ ├── SimpleCappedOfferingFactory.json │ └── Template.json ├── contracts ├── Compliance.sol ├── Customers.sol ├── Migrations.sol ├── NameSpaceRegistrar.sol ├── OfferingFactories │ ├── SimpleCappedOffering.sol │ └── SimpleCappedOfferingFactory.sol ├── SafeMath.sol ├── SecurityToken.sol ├── SecurityTokenRegistrar.sol ├── Template.sol ├── Test Contracts │ └── SecurityTokenMOCK.sol └── interfaces │ ├── ICompliance.sol │ ├── ICustomers.sol │ ├── IERC20.sol │ ├── IOfferingFactory.sol │ ├── ISecurityToken.sol │ ├── ISecurityTokenRegistrar.sol │ └── ITemplate.sol ├── docs ├── INVESTORS.md ├── ISSUERS.md ├── KYCProviders.md ├── Roles.md ├── TEMPLATES.md └── Types.md ├── flat ├── Compliance.sol ├── Customers.sol ├── Migrations.sol ├── OfferingFactories │ ├── SimpleCappedOffering.sol │ └── SimpleCappedOfferingFactory.sol ├── SafeMath.sol ├── SecurityToken.sol ├── SecurityTokenRegistrar.sol └── Template.sol ├── migrations └── 1_deploy_contracts.js ├── package.json ├── sample-keystore ├── sample-pass ├── scripts ├── coverage.sh ├── test.sh └── tokenRegistrations.sh ├── test ├── Customers.js ├── ERC20.js ├── SafeMath.js ├── SecurityToken.js ├── SecurityTokenRegistrar.js ├── Template.js └── helpers │ ├── expectRevert.js │ ├── latestTime.js │ ├── mockContracts │ ├── PolyTokenMock.sol │ └── SafeMathMock.sol │ ├── signData.js │ ├── testprivateKey.js │ ├── time.js │ └── utils.js ├── truffle.js ├── whitepaper.pdf └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "stage-3"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /migrations/* 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.DS_Store 3 | .npm-debug.log 4 | coverage/ 5 | coverageEnv/ 6 | coverage.json 7 | .idea 8 | 9 | infura_privKey 10 | infura_apiKey 11 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | 'interfaces/ICompliance.sol', 4 | 'interfaces/ICustomers.sol', 5 | 'interfaces/IERC20.sol', 6 | 'interfaces/ISecurityToken.sol', 7 | 'interfaces/ISTO.sol', 8 | 'interfaces/ISecurityTokenRegistrar.sol', 9 | 'interfaces/ITemplate.sol' 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom-rules-filename": null, 3 | "rules": { 4 | "imports-on-top": true, 5 | "variable-declarations": true, 6 | "array-declarations": true, 7 | "operator-whitespace": false, 8 | "lbrace": true, 9 | "mixedcase": false, 10 | "camelcase": true, 11 | "uppercase": true, 12 | "no-with": true, 13 | "no-empty-blocks": true, 14 | "no-unused-vars": true, 15 | "double-quotes": true, 16 | "indentation": true, 17 | "whitespace": true, 18 | "deprecated-suicide": true, 19 | "pragma-on-top": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | 6 | cache: 7 | directories: 8 | - "node_modules" 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Polymath Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PIPs/PIPS/pip-1.md: -------------------------------------------------------------------------------- 1 | PIP: 1 2 | Title: PIP Purpose and Guidelines 3 | Status: Active 4 | Type: Core 5 | Author: Pablo Ruiz , Satyam Agrawal 6 | Created: 2018-02-02 7 | 8 | What is a PIP? 9 | -------------- 10 | 11 | PIP stands for Polymath Improvement Proposal. A PIP is a design document providing information to the Polymath community, or describing a new feature for Polymath or its processes or environment or Security Token Proposals. The PIP should provide a concise technical specification of the feature and a rationale for the feature. The PIP author is responsible for building consensus within the community and documenting dissenting opinions. 12 | 13 | PIP Rational 14 | ------------ 15 | 16 | We intend PIPs to be the primary mechanisms for proposing new features and new Security Token Proposals, for collecting community input on an issue, and for documenting the design decisions that have gone into Polymath. Because the PIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal. 17 | 18 | For Polymath implementers, PIPs are a convenient way to track the progress of their implementation. Ideally each implementation maintainer would list the PIPs that they have implemented. This will give end users a convenient way to know the current status of a given implementation or library. 19 | 20 | PIP Categories 21 | --------- 22 | 23 | There are two categories of PIP: 24 | 25 | - A **Core PIP** describes any change that affects most or all Polymath implementations, such as a change to the Core Polymath smart contracts, proposed application standards/conventions, or any change or addition that affects the interoperability of smart contracts using the Polymath standard. 26 | 27 | - **STO PIP** application-level standards and conventions around the development of Security Token Offerings (STOs) that implement the underlying Core smart contracts from Polymath. 28 | 29 | PIP Work Flow 30 | ------------- 31 | 32 | The PIP repository Collaborators change the PIPs status. Please send all PIP-related email to the PIP Collaborators, which is listed under PIP Editors below. Also see PIP Editor Responsibilities & Workflow. 33 | 34 | The PIP process begins with a new idea for Polymath. It is highly recommended that a single PIP contain a single key proposal or new idea. The more focused the PIP, the more successful it tends to be. A change that affects or defines a standard for multiple apps to use requires a PIP. The PIP editor reserves the right to reject PIP proposals if they appear too unfocused or too broad. If in doubt, split your PIP into several well-focused ones. 35 | For instance: Proposing a new STO that deals with Securities that pay dividends would require both a PIP for the STO and a Core PIP for changing the underlying mechanisms of the SecurityToken smart contract. 36 | 37 | Each PIP must have a champion - someone who writes the PIP using the style and format described below, shepherds the discussions in the appropriate forums, and attempts to build community consensus around the idea. 38 | 39 | Vetting an idea publicly before going as far as writing an PIP is meant to save the potential author time. Asking the Polymath community first if an idea is original helps prevent too much time being spent on something that is guaranteed to be rejected based on prior discussions (searching the Internet does not always do the trick). It also helps to make sure the idea is applicable to the entire community and not just the author. Just because an idea sounds good to the author does not mean it will work for most people in most areas where Polymath is used. Examples of appropriate public forums to gauge interest around your PIP include [the Issues section of this repository], and [one of the Polymath Gitter chat rooms]. In particular, [the Issues section of this repository] is an excellent place to discuss your proposal with the community and start creating more formalized language around your PIP. 40 | 41 | Once the champion has asked the Polymath community whether an idea has any chance of acceptance a draft PIP should be presented as a [pull request]. This gives the author a chance to continuously edit the draft PIP for proper formatting and quality. This also allows for further public comment and the author of the PIP to address concerns about the proposal. 42 | 43 | If the PIP collaborators approve, the PIP editor will assign the PIP a number (generally the issue or PR number related to the PIP), give it status “Draft”, and add it to the git repository. The PIP editor will not unreasonably deny a PIP. Reasons for denying PIP status include duplication of effort, being technically unsound, not providing proper motivation or addressing backwards compatibility, or not in keeping with the Polymath philosophy. 44 | 45 | PIPs consist of three parts, a design document, implementation, and finally if warranted an update to the [formal specification]. The PIP should be reviewed and accepted before an implementation is begun, unless an implementation will aid people in studying the PIP. 46 | 47 | For a PIP to be accepted it must meet certain minimum criteria. It must be a clear and complete description of the proposed enhancement. The enhancement must represent a net improvement. The proposed implementation, if applicable, must be solid and must not complicate the protocol unduly. 48 | 49 | Once a PIP has been accepted, the implementations must be completed. When the implementation is complete and accepted by the community, the status will be changed to “Final”. 50 | 51 | A PIP can also be assigned status “Deferred”. The PIP author or editor can assign the PIP this status when no progress is being made on the PIP. Once a PIP is deferred, the PIP editor can re-assign it to draft status. 52 | 53 | An PIP can also be “Rejected”. Perhaps after all is said and done it was not a good idea. It is still important to have a record of this fact. 54 | 55 | PIPs can also be superseded by a different PIP, rendering the original obsolete. 56 | 57 | Some Informational and Process PIPs may also have a status of “Active” if they are never meant to be completed. E.g. PIP 1 (this PIP). 58 | 59 | What belongs in a successful PIP? 60 | --------------------------------- 61 | 62 | Each PIP parts are explained in [pip-X.md](https://github.com/PolymathNetwork/polymath-core/pull/126/pip-X.md) template file. 63 | 64 | Transferring PIP Ownership 65 | -------------------------- 66 | 67 | It occasionally becomes necessary to transfer ownership of PIPs to a new champion. In general, we'd like to retain the original author as a co-author of the transferred PIP, but that's really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the PIP process, or has fallen off the face of the 'net (i.e. is unreachable or not responding to email). A bad reason to transfer ownership is because you don't agree with the direction of the PIP. We try to build consensus around a PIP, but if that's not possible, you can always submit a competing PIP. 68 | 69 | If you are interested in assuming ownership of a PIP, send a message asking to take over, addressed to both the original author and the PIP editor. If the original author doesn't respond to email in a timely manner, the PIP editor will make a unilateral decision (it's not like such decisions can't be reversed :). 70 | 71 | PIP Editors 72 | ----------- 73 | 74 | The current PIP editors are 75 | 76 | ` * Satyam Agrawal (@satyamakgec)` 77 | 78 | ` * Pablo Ruiz (@pabloruiz55)` 79 | 80 | PIP Editor Responsibilities and Workflow 81 | -------------------------------------- 82 | 83 | For each new PIP that comes in, an editor does the following: 84 | 85 | - Read the PIP to check if it is ready: sound and complete. The ideas must make technical sense, even if they don't seem likely to be accepted. 86 | - The title should accurately describe the content. 87 | - Edit the PIP for language (spelling, grammar, sentence structure, etc.), markup (Github flavored Markdown), code style 88 | 89 | If the PIP isn't ready, the editor will send it back to the author for revision, with specific instructions. 90 | 91 | Once the PIP is ready for the repository, the PIP editor will: 92 | 93 | - Assign an PIP number (generally the PR number or, if preferred by the author, the Issue # if there was discussion in the Issues section of this repository about this PIP) 94 | 95 | 96 | 97 | - Accept the corresponding pull request 98 | 99 | 100 | 101 | - List the PIP in [README.md] 102 | 103 | 104 | 105 | - Send a message back to the PIP author with next step. 106 | 107 | Many PIPs are written and maintained by developers with write access to the Polymath codebase. The PIP editors monitor PIP changes, and correct any structure, grammar, spelling, or markup mistakes we see. 108 | 109 | The editors don't pass judgment on PIPs. We merely do the administrative & editorial part. 110 | 111 | History 112 | ------- 113 | 114 | This document was derived heavily from [Ethereum's EIP-1] which in turn was derived heavily from [Bitcoin's BIP-0001] written by Amir Taaki which in turn was derived from [Python's PEP-0001]. In many places text was simply copied and modified. 115 | 116 | Copyright 117 | --------- 118 | 119 | Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). 120 | -------------------------------------------------------------------------------- /PIPs/PIPS/pip-1/process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolymathNetwork/polymath-core-deprecated/40aec71d728413ae2f9c022c252254ffadcc86e4/PIPs/PIPS/pip-1/process.png -------------------------------------------------------------------------------- /PIPs/README.md: -------------------------------------------------------------------------------- 1 | # PIPs (Under construction) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/polymath-PIPs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 2 | Polymath Improvement Proposals (PIPs) describe standards for the Polymath platform, including core protocol specifications and Security Token Offerings standards. 3 | 4 | # Contributing 5 | First review [PIP-1](PIPS/pip-1.md). Then clone the repository and add your PIP to it. There is a [template PIP here](pip-X.md). Then submit a Pull Request to Polymath's [PIPs repository](https://github.com/Polymath/PIPs). 6 | 7 | # PIP status terms 8 | * **Draft** - a PIP that is open for consideration 9 | * **Accepted** - a PIP that is planned for immediate adoption, i.e. expected to be included in the next Polymath release. 10 | * **Final** - a PIP that has been adopted in a previous release. 11 | * **Deferred** - a PIP that is not being considered for immediate adoption. May be reconsidered in the future for a subsequent release. 12 | -------------------------------------------------------------------------------- /PIPs/pip-X.md: -------------------------------------------------------------------------------- 1 | Suggested Template for new PIPs. 2 | Raised PIPs should not affect the current protocol version. 3 | 4 | ### Guidelines 5 | When opening a pull request to submit your PIP please use an abbreviated filename pip-draft_title_abbrev.md. 6 | PIP number will be assigned by an editor. 7 | The title should be 44 characters or less. 8 | 9 | 10 | ## Preface 11 | 12 | PIP: 13 | Title: 14 | Author: 15 | Category (*only required for Standard Track): < STO | CORE > 16 | Status: Draft 17 | Created: 18 | Requires (*optional): 19 | Replaces (*optional): 20 | 21 | ## Synopsis 22 | Provide a simplified and layman-accessible explanation of the PIP. 23 | 24 | ## Abstract 25 | Please provide a short (~200 word) description of the technical issue being addressed. 26 | 27 | ## Motivation 28 | What motivates you to raise the PIP.PIP submissions without sufficient motivation may be rejected outright. 29 | If it is a CORE PIP then explain why the current version of the protocol needs to be modified. 30 | 31 | ## Specification 32 | The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current polymath platforms (polymath.js ...). If Core contracts need to be modified, explain why and how you would perform those modifications. If necessary submit an additional PIP that deals with CORE modifications. 33 | 34 | ## Rationale 35 | The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion. 36 | 37 | ## Backwards Compatibility 38 | All PIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The PIP must explain how the author proposes to deal with these incompatibilities. PIP submissions without a sufficient backwards compatibility treatise may be rejected outright. 39 | 40 | ## Test Cases 41 | Test cases for an implementation are mandatory for PIPs that are affecting how Core smart contracts work and that would require upgrading or replacing existing Core contracts. 42 | 43 | ## Implementation 44 | The implementations must be completed before any PIP is given status "Final", but it need not be completed before the PIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code. -------------------------------------------------------------------------------- /Polymath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolymathNetwork/polymath-core-deprecated/40aec71d728413ae2f9c022c252254ffadcc86e4/Polymath.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/PolymathNetwork/polymath-core.svg?token=Urvmqzpy4pAxp6EpzZd6&branch=master)](https://travis-ci.com/PolymathNetwork/polymath-core) 2 | 3 | 4 | 5 | 6 | ![Polymath](Polymath.png) 7 | 8 | # ATTENTION! 9 | This repository for Polymath Core has been deprecated and will not be maintained moving forward. 10 | Please refer to https://github.com/PolymathNetwork/polymath-core. 11 | 12 | # Polymath core 13 | 14 | The polymath core smart contracts provide a system for launching regulatory 15 | compliant securities tokens on a decentralized blockchain. 16 | 17 | [Read the whitepaper](whitepaper.pdf) 18 | 19 | ## Live on Ethereum Mainnet 20 | 21 | | Contract | Address | 22 | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | 23 | | [PolyToken](./contracts/PolyToken.sol) | [0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC](https://etherscan.io/address/0x9992eC3cF6A55b00978cdDF2b27BC6882d88D1eC) | 24 | | [Compliance](./contracts/Compliance.sol) | [0x076719c05961a0c3398e558e2199085d32717ca6](https://etherscan.io/address/0x076719c05961a0c3398e558e2199085d32717ca6) | 25 | | [Customers](./contracts/Customers.sol) | [ 0xeb30a60c199664ab84dec3f8b72de3badf1837f5](https://etherscan.io/address/0xeb30a60c199664ab84dec3f8b72de3badf1837f5) | 26 | | [SecurityTokenRegistrar](./contracts/SecurityTokenRegistrar.sol) | [0x56e30b617c8b4798955b6be6fec706de91352ed0](https://etherscan.io/address/0x56e30b617c8b4798955b6be6fec706de91352ed0) | 27 | 28 | 29 | ## Live on Ropsten testnet 30 | 31 | | Contract | Address | 32 | | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | 33 | | [PolyToken](./contracts/PolyToken.sol) | [0x96a62428509002a7ae5f6ad29e4750d852a3f3d7](https://ropsten.etherscan.io/address/0x96a62428509002a7ae5f6ad29e4750d852a3f3d7) | 34 | | [Compliance](./contracts/Compliance.sol) | [0x7a3a0acde71718c0224e677f9586a18701e11815](https://ropsten.etherscan.io/address/0x7a3a0acde71718c0224e677f9586a18701e11815) | 35 | | [Customers](./contracts/Customers.sol) | [0x9e892f4e7ac1e13778599a1506cf68ff2e3cb938](https://ropsten.etherscan.io/address/0x9e892f4e7ac1e13778599a1506cf68ff2e3cb938) | 36 | | [SecurityTokenRegistrar](./contracts/SecurityTokenRegistrar.sol) | [0xe93d40736a9537308d822bf15edae634a24be179](https://ropsten.etherscan.io/address/0xe93d40736a9537308d822bf15edae634a24be179) | 37 | | [NameSpaceRegistrar](./contracts/NameSpaceRegistrar.sol) | [0x4e875a08616565b9063ab240bc2dd72788a656e0](https://ropsten.etherscan.io/address/0x4e875a08616565b9063ab240bc2dd72788a656e0) | 38 | 39 | ## Setup 40 | 41 | The smart contracts are written in [Solidity][solidity] and tested/deployed 42 | using [Truffle][truffle] version 4.0.0. The new version of Truffle doesn't 43 | require testrpc to be installed separately so you can just do use the following: 44 | 45 | ```bash 46 | # Install Truffle package globally: 47 | $ npm install -g truffle 48 | 49 | # Install local node dependencies: 50 | $ npm install 51 | ``` 52 | 53 | ## Testing 54 | 55 | To test the code simply run: 56 | 57 | ``` 58 | $ npm run test 59 | ``` 60 | 61 | ## Contributing 62 | 63 | We're always looking for developers to join the polymath network. To do so we 64 | encourage developers to contribute by creating Security Token Offering contracts 65 | (STO) which can be used by issuers to raise funds. If your contract is used, you 66 | can earn POLY fees directly through the contract, and additional bonuses through 67 | the Polymath reserve fund. 68 | 69 | If you would like to apply directly to our STO contract development team, please 70 | send your resume and/or portfolio to careers@polymath.network. 71 | 72 | ### Styleguide 73 | 74 | The polymath-core repo follows the style guide overviewed here: 75 | http://solidity.readthedocs.io/en/develop/style-guide.html 76 | 77 | [polymath]: https://polymath.network 78 | [ethereum]: https://www.ethereum.org/ 79 | [solidity]: https://solidity.readthedocs.io/en/develop/ 80 | [truffle]: http://truffleframework.com/ 81 | [testrpc]: https://github.com/ethereumjs/testrpc 82 | -------------------------------------------------------------------------------- /contracts/Customers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /* 4 | Polymath customer registry is used to ensure regulatory compliance 5 | of the investors, provider, and issuers. The customers registry is a central 6 | place where ethereum addresses can be whitelisted to purchase certain security 7 | tokens based on their verifications by providers. 8 | */ 9 | 10 | import './interfaces/IERC20.sol'; 11 | import './interfaces/ICustomers.sol'; 12 | 13 | /** 14 | * @title Customers 15 | * @dev Contract use to register the user on the Polymath platform 16 | */ 17 | 18 | contract Customers is ICustomers { 19 | 20 | string public VERSION = "2"; 21 | 22 | IERC20 POLY; // Instance of the POLY token 23 | 24 | struct Customer { // Structure use to store the details of the customers 25 | bytes32 countryJurisdiction; // Customers country jurisdiction as ex - ISO3166 26 | bytes32 divisionJurisdiction; // Customers sub-division jurisdiction as ex - ISO3166 27 | uint256 joined; // Timestamp when customer register 28 | uint8 role; // Role of the customer 29 | bool accredited; // Accrediation status of the customer 30 | bytes32 proof; // Proof for customer 31 | uint256 expires; // Timestamp when customer verification expires 32 | } 33 | 34 | mapping(address => mapping(address => Customer)) public customers; // Customers (kyc provider address => customer address) 35 | mapping(address => mapping(uint256 => bool)) public nonceMap; // Map of used nonces by customer 36 | 37 | struct Provider { // KYC/Accreditation Provider 38 | string name; // Name of the provider 39 | uint256 joined; // Timestamp when provider register 40 | bytes32 details; // Details of provider 41 | uint256 fee; // Fee charged by the KYC providers 42 | } 43 | 44 | mapping(address => Provider) public providers; // KYC/Accreditation Providers 45 | 46 | // Notifications 47 | event LogNewProvider(address indexed providerAddress, string name, bytes32 details, uint256 _fee); 48 | event LogCustomerVerified(address indexed customer, address indexed provider, uint8 role); 49 | 50 | // Modifier 51 | modifier onlyProvider() { 52 | require(providers[msg.sender].details != 0x0); 53 | _; 54 | } 55 | 56 | /** 57 | * @dev Constructor 58 | */ 59 | function Customers(address _polyTokenAddress) public { 60 | POLY = IERC20(_polyTokenAddress); 61 | } 62 | 63 | /** 64 | * @dev Allow new provider applications 65 | * @param _name The provider's name 66 | * @param _details A SHA256 hash of the new providers details 67 | * @param _fee The fee charged for customer verification 68 | */ 69 | function newProvider(string _name, bytes32 _details, uint256 _fee) public returns (bool success) { 70 | require(_details != 0x0); 71 | require(providers[msg.sender].details == 0x0); 72 | providers[msg.sender] = Provider(_name, now, _details, _fee); 73 | LogNewProvider(msg.sender, _name, _details, _fee); 74 | return true; 75 | } 76 | 77 | /** 78 | * @dev Change a providers fee 79 | * @param _newFee The new fee of the provider 80 | */ 81 | function changeFee(uint256 _newFee) onlyProvider public returns (bool success) { 82 | providers[msg.sender].fee = _newFee; 83 | return true; 84 | } 85 | 86 | 87 | /** 88 | * @dev Verify an investor 89 | * @param _customer The customer's public key address 90 | * @param _countryJurisdiction The jurisdiction country code of the customer 91 | * @param _divisionJurisdiction The jurisdiction subdivision code of the customer 92 | * @param _role The type of customer - investor:1, delegate:2, issuer:3, marketmaker:4, etc. 93 | * @param _accredited Whether the customer is accredited or not (only applied to investors) 94 | * @param _expires The time the verification expires 95 | * @param _nonce nonce of signature (avoid replay attack) 96 | * @param _v customer signature 97 | * @param _r customer signature 98 | * @param _s customer signature 99 | */ 100 | function verifyCustomer( 101 | address _customer, 102 | bytes32 _countryJurisdiction, 103 | bytes32 _divisionJurisdiction, 104 | uint8 _role, 105 | bool _accredited, 106 | uint256 _expires, 107 | uint _nonce, 108 | uint8 _v, 109 | bytes32 _r, 110 | bytes32 _s 111 | ) public onlyProvider returns (bool success) 112 | { 113 | require(_expires > now); 114 | require(nonceMap[_customer][_nonce] == false); 115 | nonceMap[_customer][_nonce] = true; 116 | bytes32 hash = keccak256(this, msg.sender, _countryJurisdiction, _divisionJurisdiction, _role, _accredited, _nonce); 117 | require(ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) == _customer); 118 | require(POLY.transferFrom(_customer, msg.sender, providers[msg.sender].fee)); 119 | customers[msg.sender][_customer].countryJurisdiction = _countryJurisdiction; 120 | customers[msg.sender][_customer].divisionJurisdiction = _divisionJurisdiction; 121 | customers[msg.sender][_customer].role = _role; 122 | customers[msg.sender][_customer].accredited = _accredited; 123 | customers[msg.sender][_customer].expires = _expires; 124 | LogCustomerVerified(_customer, msg.sender, _role); 125 | return true; 126 | } 127 | 128 | /////////////////// 129 | /// GET Functions 130 | ////////////////// 131 | 132 | /** 133 | * @dev Get customer attestation data by KYC provider and customer ethereum address 134 | * @param _provider Address of the KYC provider. 135 | * @param _customer Address of the customer ethereum address 136 | */ 137 | function getCustomer(address _provider, address _customer) public view returns ( 138 | bytes32, 139 | bytes32, 140 | bool, 141 | uint8, 142 | uint256 143 | ) { 144 | return ( 145 | customers[_provider][_customer].countryJurisdiction, 146 | customers[_provider][_customer].divisionJurisdiction, 147 | customers[_provider][_customer].accredited, 148 | customers[_provider][_customer].role, 149 | customers[_provider][_customer].expires 150 | ); 151 | } 152 | 153 | /** 154 | * Get provider details and fee by ethereum address 155 | * @param _providerAddress Address of the KYC provider 156 | */ 157 | function getProvider(address _providerAddress) public view returns ( 158 | string name, 159 | uint256 joined, 160 | bytes32 details, 161 | uint256 fee 162 | ) { 163 | return ( 164 | providers[_providerAddress].name, 165 | providers[_providerAddress].joined, 166 | providers[_providerAddress].details, 167 | providers[_providerAddress].fee 168 | ); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | require (msg.sender == owner); 9 | _; 10 | } 11 | 12 | function Migrations() public { 13 | owner = msg.sender; 14 | } 15 | 16 | function setCompleted(uint completed)public restricted { 17 | last_completed_migration = completed; 18 | } 19 | 20 | function upgrade(address new_address)public restricted { 21 | Migrations upgraded = Migrations(new_address); 22 | upgraded.setCompleted(last_completed_migration); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/NameSpaceRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /* 4 | Allows issuers to reserve their token symbols under a given namespace ahead 5 | of actually generating their security token. 6 | SecurityTokenRegistrar would reference this contract and ensure that any token symbols 7 | registered here can only be created by their owner. 8 | */ 9 | 10 | /** 11 | * @title NameSpaceRegistrar 12 | * @dev Contract use to register the security token symbols 13 | */ 14 | 15 | contract NameSpaceRegistrar { 16 | 17 | string public VERSION = "2"; 18 | 19 | struct SymbolDetails { 20 | address owner; 21 | uint256 timestamp; 22 | string description; 23 | } 24 | 25 | mapping (string => mapping (string => SymbolDetails)) symbolDetails; //Tracks details for namespace / symbol registrations 26 | mapping (address => bool) hasRegistered; //Tracks addresses that have registered tokens, only allow one registration per address 27 | mapping (address => bool) public admins; //Track valid admin addresses 28 | 29 | event AdminChange(address indexed _admin, bool _valid); 30 | event RegisteredToken(string _nameSpace, string _symbol, string _description, string _contact, address indexed _owner, address indexed _admin, uint256 _timestamp); 31 | event TokenOwnerChange(string _nameSpace, string _symbol, address indexed _oldOwner, address indexed _newOwner); 32 | 33 | // Check that msg.sender is an admin 34 | modifier onlyAdmin { 35 | require(admins[msg.sender]); 36 | _; 37 | } 38 | 39 | /** 40 | * @dev Constructor - sets the admin 41 | * the creation of the security token 42 | */ 43 | function NameSpaceRegistrar() public 44 | { 45 | admins[msg.sender] = true; 46 | } 47 | 48 | /** 49 | * @dev allows the admin address to set a new admin 50 | * @param _admin admin address 51 | * @param _valid bool to indicate whether admin address is allowed 52 | */ 53 | function changeAdmin(address _admin, bool _valid) onlyAdmin public { 54 | //You can't remove yourself as an admin 55 | require(msg.sender != _admin); 56 | admins[_admin] = _valid; 57 | AdminChange(_admin, _valid); 58 | } 59 | 60 | /** 61 | * @dev allows the owner of a token registration to change the owner 62 | * @param _nameSpace namespace 63 | * @param _symbol token symbol 64 | * @param _newOwner new owner 65 | */ 66 | function changeTokenOwner(string _nameSpace, string _symbol, address _newOwner) public { 67 | require(symbolDetails[_nameSpace][_symbol].owner == msg.sender); 68 | symbolDetails[_nameSpace][_symbol].owner = _newOwner; 69 | TokenOwnerChange(_nameSpace, _symbol, msg.sender, _newOwner); 70 | } 71 | 72 | /** 73 | * @dev Registers a new token symbol and owner against a specific namespace 74 | * @param _nameSpace namespace 75 | * @param _symbol token symbol 76 | * @param _description token description 77 | * @param _contact token contract details e.g. email 78 | * @param _owner owner 79 | */ 80 | function registerToken(string _nameSpace, string _symbol, string _description, string _contact, address _owner) onlyAdmin public { 81 | if(msg.sender != _owner) 82 | require(!hasRegistered[_owner]); 83 | require(_owner != address(0)); 84 | require(symbolDetails[_nameSpace][_symbol].owner == address(0)); 85 | hasRegistered[_owner] = true; 86 | symbolDetails[_nameSpace][_symbol].owner = _owner; 87 | symbolDetails[_nameSpace][_symbol].description = _description; 88 | symbolDetails[_nameSpace][_symbol].timestamp = now; 89 | RegisteredToken(_nameSpace, _symbol, _description, _contact, _owner, msg.sender, now); 90 | } 91 | 92 | /** 93 | * @dev Returns the owner and timestamp for a given symbol, under a given namespace - can be called by other contracts 94 | * @param _nameSpace namespace 95 | * @param _symbol symbol 96 | */ 97 | function getDetails(string _nameSpace, string _symbol) view public returns (address, uint256) { 98 | return (symbolDetails[_nameSpace][_symbol].owner, symbolDetails[_nameSpace][_symbol].timestamp); 99 | } 100 | 101 | /** 102 | * @dev Returns the description and contact details for a given symbol, under a given namespace - cannot be called by other contracts 103 | * @param _nameSpace namespace 104 | * @param _symbol symbol 105 | */ 106 | function getDescription(string _nameSpace, string _symbol) view public returns (string) { 107 | return symbolDetails[_nameSpace][_symbol].description; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /contracts/OfferingFactories/SimpleCappedOffering.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../interfaces/ISecurityToken.sol'; 4 | import '../interfaces/IERC20.sol'; 5 | import '../SafeMath.sol'; 6 | 7 | contract SimpleCappedOffering { 8 | 9 | using SafeMath for uint256; 10 | string public VERSION = "1"; 11 | 12 | ISecurityToken public SecurityToken; 13 | 14 | uint256 public maxPoly; // Maximum Poly limit raised by the offering contract 15 | uint256 public polyRaised; // Variable to track the poly raised 16 | uint256 public startTime; // Unix timestamp to start the offering 17 | uint256 public endTime; // Unix timestamp to end the offering 18 | uint256 public exchangeRatePolyToken; // Fix rate of 1 security token in terms of POLY 19 | 20 | uint256 public securityTokensSold; // Amount of security tokens sold through the STO 21 | 22 | ///////////// 23 | // Constants 24 | ///////////// 25 | 26 | uint256 public constant DECIMALSFACTOR = 10**uint256(18); 27 | 28 | uint256 public constant TOKENS_MAX_TOTAL = 1000000 * DECIMALSFACTOR; // 100% 29 | uint256 public constant TOKENS_STO = 500000 * DECIMALSFACTOR; // 50% 30 | uint256 public constant TOKENS_FOUNDERS = 200000 * DECIMALSFACTOR; // 20% 31 | uint256 public constant TOKENS_EARLY_INVESTORS = 200000 * DECIMALSFACTOR; // 20% 32 | uint256 public constant TOKENS_ADVISORS = 100000 * DECIMALSFACTOR; // 10% 33 | 34 | /////////////// 35 | // MODIFIERS // 36 | /////////////// 37 | 38 | modifier onlyDuringSale() { 39 | require(hasStarted() && !hasEnded()); 40 | _; 41 | } 42 | 43 | modifier onlyAfterSale() { 44 | // require finalized is stronger than hasSaleEnded 45 | require(hasEnded()); 46 | _; 47 | } 48 | 49 | //////////// 50 | // EVENTS // 51 | //////////// 52 | 53 | event LogBoughtSecurityToken(address indexed _contributor, uint256 _ployContribution, uint256 _timestamp); 54 | 55 | /** 56 | * @dev Constructor A new instance of the capped offering contract get launch 57 | * everytime when the constructor called by the factory contract 58 | * @param _startTime Unix timestamp to start the offering 59 | * @param _endTime Unix timestamp to end the offering 60 | * @param _exchangeRatePolyToken Price of one security token in terms of poly 61 | * @param _maxPoly Maximum amount of poly issuer wants to collect 62 | * @param _securityToken Address of the security token 63 | */ 64 | 65 | function SimpleCappedOffering(uint256 _startTime, uint256 _endTime, uint256 _exchangeRatePolyToken, uint256 _maxPoly, address _securityToken) public { 66 | require(_startTime >= now); 67 | require(_endTime > _startTime); 68 | require(_exchangeRatePolyToken > 0); 69 | require(_securityToken != address(0)); 70 | 71 | //TOKENS_MAX_TOTAL MUST BE equal to all other token token allocations combined 72 | require(TOKENS_STO.add(TOKENS_FOUNDERS).add(TOKENS_EARLY_INVESTORS).add(TOKENS_ADVISORS) == TOKENS_MAX_TOTAL); 73 | 74 | startTime = _startTime; 75 | endTime = _endTime; 76 | exchangeRatePolyToken = _exchangeRatePolyToken; 77 | maxPoly = _maxPoly; 78 | SecurityToken = ISecurityToken(_securityToken); 79 | } 80 | 81 | /** 82 | * @dev `buy` Facilitate the buying of SecurityToken in exchange of POLY 83 | * @param _polyContributed Amount of POLY contributor want to invest. 84 | * @return bool 85 | */ 86 | function buy(uint256 _polyContributed) public onlyDuringSale returns(bool) { 87 | require(_polyContributed > 0); 88 | require(validPurchase(_polyContributed)); 89 | uint256 _amountOfSecurityTokens = _polyContributed.div(exchangeRatePolyToken); 90 | 91 | // Make sure we don't sell more tokens than those available to the STO 92 | // TBD change this so we can sell the difference. 93 | require (securityTokensSold.add(_amountOfSecurityTokens) <= TOKENS_STO); 94 | 95 | require(SecurityToken.issueSecurityTokens(msg.sender, _amountOfSecurityTokens, _polyContributed)); 96 | 97 | polyRaised = polyRaised.add(_polyContributed); 98 | securityTokensSold = securityTokensSold.add(_amountOfSecurityTokens); //Keep track of tokens sold 99 | 100 | LogBoughtSecurityToken(msg.sender, _polyContributed, now); 101 | 102 | return true; 103 | } 104 | 105 | /** 106 | * @dev Use to validate the poly contribution 107 | * If issuer sets the capping over the offering contract then raised amount should 108 | * always less than or equal to the maximum amount set (maxPoly) 109 | * @param _polyContributed Amount of POLY contributor want to invest 110 | * @return bool 111 | */ 112 | function validPurchase(uint256 _polyContributed) internal view returns(bool) { 113 | bool reachedCap = maxPoly > 0 && polyRaised.add(_polyContributed) <= maxPoly; 114 | return (reachedCap); 115 | } 116 | 117 | // 118 | //Helper functions for onlyDuringSale / onlyAfterSale modifiers 119 | // 120 | 121 | // @return true if STO has ended 122 | function hasEnded() public constant returns (bool) { 123 | return now > endTime; 124 | } 125 | 126 | // @return true if STO has started 127 | function hasStarted() public constant returns (bool) { 128 | return now >= startTime; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /contracts/OfferingFactories/SimpleCappedOfferingFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './SimpleCappedOffering.sol'; 4 | import '../interfaces/IOfferingFactory.sol'; 5 | 6 | /** 7 | * @dev Highly Recommended - Only a sample STO factory Not used for mainnet !! 8 | */ 9 | 10 | contract SimpleCappedOfferingFactory is IOfferingFactory { 11 | 12 | using SafeMath for uint256; 13 | string public VERSION = "1"; 14 | 15 | ISecurityToken public SecurityToken; 16 | 17 | uint256 public fee = 100; 18 | uint8 public quorum = 10; 19 | uint256 public vestingPeriod = 8888888; 20 | bytes32 public description = "Capped"; 21 | uint256 public fxPolyToken; 22 | 23 | address public owner; 24 | 25 | function SimpleCappedOfferingFactory() public { 26 | owner = msg.sender; 27 | } 28 | 29 | /** 30 | * @dev It facilitate the creation of the STO contract with essentials parameters 31 | * @param _startTime Unix timestamp to start the offering 32 | * @param _endTime Unix timestamp to end the offering 33 | * @param _polyTokenRate Price of one security token in terms of poly 34 | * @param _maxPoly Maximum amount of poly issuer wants to collect 35 | * @return address Address of the new offering instance 36 | */ 37 | function createOffering( 38 | uint256 _startTime, 39 | uint256 _endTime, 40 | uint256 _polyTokenRate, 41 | uint256 _maxPoly 42 | ) public returns (address) 43 | { 44 | return new SimpleCappedOffering(_startTime, _endTime, _polyTokenRate, _maxPoly, msg.sender); 45 | } 46 | 47 | /** 48 | * @dev `getUsageDetails` is a function to get all the details on factory usage fees 49 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, string description 50 | */ 51 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, bytes32) { 52 | return (fee, quorum, vestingPeriod, owner, description); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * SafeMath 5 | * Copyright (c) 2016 Smart Contract Solutions, Inc. 6 | * Released under the MIT License (MIT) 7 | */ 8 | 9 | /// @title Math operations with safety checks 10 | library SafeMath { 11 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 12 | uint256 c = a * b; 13 | assert(a == 0 || c / a == b); 14 | return c; 15 | } 16 | 17 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 18 | // assert(b > 0); // Solidity automatically throws when dividing by 0 19 | uint256 c = a / b; 20 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 21 | return c; 22 | } 23 | 24 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 25 | assert(b <= a); 26 | return a - b; 27 | } 28 | 29 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 30 | uint256 c = a + b; 31 | assert(c >= a); 32 | return c; 33 | } 34 | 35 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 36 | return a >= b ? a : b; 37 | } 38 | 39 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 40 | return a < b ? a : b; 41 | } 42 | 43 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 44 | return a >= b ? a : b; 45 | } 46 | 47 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 48 | return a < b ? a : b; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /contracts/SecurityTokenRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /* 4 | The Polymath Security Token Registrar provides a way to lookup security token details 5 | from a single place and allows wizard creators to earn POLY fees by uploading to the 6 | registrar. 7 | */ 8 | import './interfaces/ISecurityTokenRegistrar.sol'; 9 | import './interfaces/IERC20.sol'; 10 | import './SecurityToken.sol'; 11 | 12 | /** 13 | * @title SecurityTokenRegistrar 14 | * @dev Contract use to register the security token on Polymath platform 15 | */ 16 | 17 | contract SecurityTokenRegistrar is ISecurityTokenRegistrar { 18 | 19 | string public VERSION = "2"; 20 | IERC20 public PolyToken; // Address of POLY token 21 | address public polyCustomersAddress; // Address of the polymath-core Customers contract address 22 | address public polyComplianceAddress; // Address of the polymath-core Compliance contract address 23 | 24 | struct NameSpaceData { 25 | address owner; 26 | uint256 fee; 27 | } 28 | 29 | // Security Token 30 | struct SecurityTokenData { // A structure that contains the specific info of each ST 31 | string nameSpace; 32 | string ticker; 33 | address owner; 34 | uint8 securityType; 35 | } 36 | 37 | mapping (string => NameSpaceData) nameSpaceData; // Mapping from nameSpace to owner / fee of nameSpace 38 | mapping (address => SecurityTokenData) securityTokens; // Mapping from securityToken address to data about the securityToken 39 | mapping (string => mapping (string => address)) tickers; // Mapping from nameSpace, to a mapping of ticker name to correspondong securityToken addresses 40 | 41 | event LogNewSecurityToken(string _nameSpace, string _ticker, address indexed _securityTokenAddress, address indexed _owner, uint8 _type); 42 | event LogNameSpaceCreated(string _nameSpace, address _owner, uint256 _fee); 43 | event LogNameSpaceChange(string _nameSpace, address _newOwner, uint256 _newFee); 44 | 45 | /** 46 | * @dev Constructor use to set the essentials addresses to facilitate 47 | * the creation of the security token 48 | */ 49 | function SecurityTokenRegistrar( 50 | address _polyTokenAddress, 51 | address _polyCustomersAddress, 52 | address _polyComplianceAddress 53 | ) public 54 | { 55 | require(_polyTokenAddress != address(0)); 56 | require(_polyCustomersAddress != address(0)); 57 | require(_polyComplianceAddress != address(0)); 58 | PolyToken = IERC20(_polyTokenAddress); 59 | polyCustomersAddress = _polyCustomersAddress; 60 | polyComplianceAddress = _polyComplianceAddress; 61 | } 62 | 63 | /** 64 | * @dev Creates a securityToken name space 65 | * @param _nameSpace Name space string 66 | * @param _fee Fee for this name space 67 | */ 68 | function createNameSpace(string _nameSpace, uint256 _fee) public { 69 | require(bytes(_nameSpace).length > 0); 70 | string memory nameSpace = lower(_nameSpace); 71 | require(nameSpaceData[nameSpace].owner == address(0)); 72 | nameSpaceData[nameSpace].owner = msg.sender; 73 | nameSpaceData[nameSpace].fee = _fee; 74 | LogNameSpaceCreated(nameSpace, msg.sender, _fee); 75 | } 76 | 77 | /** 78 | * @dev changes a string to lower case 79 | * @param _base string to change 80 | */ 81 | function lower(string _base) internal pure returns (string) { 82 | bytes memory _baseBytes = bytes(_base); 83 | for (uint i = 0; i < _baseBytes.length; i++) { 84 | bytes1 b1 = _baseBytes[i]; 85 | if (b1 >= 0x41 && b1 <= 0x5A) { 86 | b1 = bytes1(uint8(b1)+32); 87 | } 88 | _baseBytes[i] = b1; 89 | } 90 | return string(_baseBytes); 91 | } 92 | 93 | /** 94 | * @dev Changes name space fee 95 | * @param _nameSpace Name space string 96 | * @param _fee New fee for security token creation for this name space 97 | */ 98 | function changeNameSpace(string _nameSpace, uint256 _fee) public { 99 | string memory nameSpace = lower(_nameSpace); 100 | require(msg.sender == nameSpaceData[nameSpace].owner); 101 | nameSpaceData[nameSpace].fee = _fee; 102 | LogNameSpaceChange(nameSpace, msg.sender, _fee); 103 | } 104 | 105 | /** 106 | * @dev Creates a new Security Token and saves it to the registry 107 | * @param _nameSpaceName Name space for this security token 108 | * @param _name Name of the security token 109 | * @param _ticker Ticker name of the security 110 | * @param _totalSupply Total amount of tokens being created 111 | * @param _decimals Decimals value for token 112 | * @param _owner Ethereum public key address of the security token owner 113 | * @param _type Type of security being tokenized 114 | */ 115 | function createSecurityToken ( 116 | string _nameSpaceName, 117 | string _name, 118 | string _ticker, 119 | uint256 _totalSupply, 120 | uint8 _decimals, 121 | address _owner, 122 | uint8 _type 123 | ) external 124 | { 125 | require(_totalSupply > 0); 126 | NameSpaceData storage nameSpace = nameSpaceData[_nameSpaceName]; 127 | require(tickers[_nameSpaceName][_ticker] == address(0)); 128 | require(nameSpace.owner != address(0)); 129 | require(_owner != address(0)); 130 | require(bytes(_name).length > 0 && bytes(_ticker).length > 0); 131 | require(PolyToken.transferFrom(msg.sender, nameSpace.owner, nameSpace.fee)); 132 | address newSecurityTokenAddress = new SecurityToken( 133 | _name, 134 | _ticker, 135 | _totalSupply, 136 | _decimals, 137 | _owner, 138 | PolyToken, 139 | polyCustomersAddress, 140 | polyComplianceAddress 141 | ); 142 | tickers[_nameSpaceName][_ticker] = newSecurityTokenAddress; 143 | securityTokens[newSecurityTokenAddress] = SecurityTokenData( 144 | _nameSpaceName, 145 | _ticker, 146 | _owner, 147 | _type 148 | ); 149 | LogNewSecurityToken(_nameSpaceName, _ticker, newSecurityTokenAddress, _owner, _type); 150 | } 151 | 152 | ////////////////////////////// 153 | ///////// Get Functions 154 | ////////////////////////////// 155 | /** 156 | * @dev Get security token address by ticker name 157 | * @param _nameSpace Name space of the Scurity token 158 | * @param _ticker Symbol of the Scurity token 159 | * @return address _ticker 160 | */ 161 | function getSecurityTokenAddress(string _nameSpace, string _ticker) public view returns (address) { 162 | return tickers[_nameSpace][_ticker]; 163 | } 164 | 165 | /** 166 | * @dev Get Security token details by its ethereum address 167 | * @param _STAddress Security token address 168 | */ 169 | function getSecurityTokenData(address _STAddress) public view returns ( 170 | string, 171 | string, 172 | address, 173 | uint8 174 | ) { 175 | return ( 176 | securityTokens[_STAddress].nameSpace, 177 | securityTokens[_STAddress].ticker, 178 | securityTokens[_STAddress].owner, 179 | securityTokens[_STAddress].securityType 180 | ); 181 | } 182 | 183 | /** 184 | * @dev Get the name space data 185 | * @param _nameSpace Name space string. 186 | */ 187 | function getNameSpaceData(string _nameSpace) public view returns(address, uint256) { 188 | string memory nameSpace = lower(_nameSpace); 189 | return ( 190 | nameSpaceData[nameSpace].owner, 191 | nameSpaceData[nameSpace].fee 192 | ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /contracts/Template.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /* 4 | Polymath compliance template is intended to ensure regulatory compliance 5 | in the jurisdictions that security tokens are being offered in. The compliance 6 | template allows security tokens to enforce purchase restrictions on chain and 7 | keep a log of documents for future auditing purposes. 8 | */ 9 | 10 | import './interfaces/ITemplate.sol'; 11 | 12 | /** 13 | * @title Template 14 | * @dev Template details used for the security token offering to ensure the regulatory compliance 15 | */ 16 | 17 | contract Template is ITemplate { 18 | 19 | string public VERSION = "1"; 20 | 21 | address public owner; // Address of the owner of template 22 | string public offeringType; // Name of the security being issued 23 | bytes32 public issuerJurisdiction; // Variable contains the jurisdiction of the issuer of the template 24 | mapping(bytes32 => bool) public allowedJurisdictions; // Mapping that contains the allowed staus of Jurisdictions 25 | mapping(bytes32 => bool) public blockedDivisionJurisdictions; // Mapping that contains the allowed staus of Jurisdictions 26 | mapping(uint8 => bool) public allowedRoles; // Mapping that contains the allowed status of Roles 27 | bytes32[] public allowedJurisdictionsList; // List of allowed jurisdiction in the template 28 | bytes32[] public blockedDivisionJurisdictionsList; // List of blocked divison jurisdiction in the template 29 | uint8[] public allowedRolesList; // List of allowed roles list 30 | bool public accredited; // Variable that define the required level of accrediation for the investor 31 | address public KYC; // Address of the KYC provider 32 | bytes32 details; // Details of the offering requirements 33 | bool finalized; // Variable to know the status of the template (complete - true, not complete - false) 34 | uint256 public expires; // Timestamp when template expires 35 | uint256 fee; // Amount of POLY to use the template (held in escrow until issuance) 36 | uint8 quorum; // Minimum percent of shareholders which need to vote to freeze 37 | uint256 vestingPeriod; // Length of time to vest funds 38 | 39 | uint removedJurisdictionsCount; // Keeps track of how many jurisdictions have been removed from allowed list for this template 40 | // Notification 41 | event DetailsUpdated(bytes32 _prevDetails, bytes32 _newDetails, uint _updateDate); 42 | event LogFinalizedTemplate(bool _finalized, uint256 _timestamp); 43 | 44 | function Template ( 45 | address _owner, 46 | string _offeringType, 47 | bytes32 _issuerJurisdiction, 48 | bool _accredited, 49 | address _KYC, 50 | bytes32 _details, 51 | uint256 _expires, 52 | uint256 _fee, 53 | uint8 _quorum, 54 | uint256 _vestingPeriod 55 | ) public 56 | { 57 | require(_KYC != address(0) && _owner != address(0)); 58 | require(_details.length > 0 && _expires > now && _issuerJurisdiction.length > 0); 59 | require(_quorum > 0 && _quorum <= 100); 60 | require(_vestingPeriod > 0); 61 | owner = _owner; 62 | offeringType = _offeringType; 63 | issuerJurisdiction = _issuerJurisdiction; 64 | accredited = _accredited; 65 | KYC = _KYC; 66 | details = _details; 67 | finalized = false; 68 | expires = _expires; 69 | fee = _fee; 70 | quorum = _quorum; 71 | vestingPeriod = _vestingPeriod; 72 | } 73 | 74 | /** 75 | * @dev `addJurisdiction` allows the adding of new jurisdictions to a template 76 | * @param _allowedJurisdictions An array of jurisdictions 77 | * @param _allowed An array of whether the jurisdiction is allowed to purchase the security or not 78 | */ 79 | function addJurisdiction(bytes32[] _allowedJurisdictions, bool[] _allowed) public { 80 | require(owner == msg.sender); 81 | require(_allowedJurisdictions.length == _allowed.length); 82 | require(!finalized); 83 | for (uint i = 0; i < _allowedJurisdictions.length; ++i) { 84 | if (!allowedJurisdictions[_allowedJurisdictions[i]] && _allowed[i]) 85 | allowedJurisdictionsList.push(_allowedJurisdictions[i]); 86 | else if (allowedJurisdictions[_allowedJurisdictions[i]] && !_allowed[i]) { 87 | removeFromJurisdictionList(_allowedJurisdictions[i]); 88 | removedJurisdictionsCount++; 89 | } 90 | 91 | allowedJurisdictions[_allowedJurisdictions[i]] = _allowed[i]; 92 | } 93 | } 94 | 95 | /** 96 | * @dev `addDivisionJurisdiction` allows the adding of new jurisdictions to a template 97 | * @param _blockedDivisionJurisdictions An array of subdivision jurisdictions 98 | * @param _blocked An array of whether the subdivision jurisdiction is blocked to purchase the security or not 99 | */ 100 | function addDivisionJurisdiction(bytes32[] _blockedDivisionJurisdictions, bool[] _blocked) public { 101 | require(owner == msg.sender); 102 | require(_blockedDivisionJurisdictions.length == _blocked.length); 103 | require(!finalized); 104 | for (uint i = 0; i < _blockedDivisionJurisdictions.length; ++i) { 105 | if (!blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] && _blocked[i]) 106 | blockedDivisionJurisdictionsList.push(_blockedDivisionJurisdictions[i]); 107 | else if (blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] && !_blocked[i]) { 108 | removeFromDivisionJurisdictionList(_blockedDivisionJurisdictions[i]); 109 | } 110 | 111 | blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] = _blocked[i]; 112 | } 113 | } 114 | 115 | /** 116 | * @dev remove the jurisdiction from the allowed list of jurisdictions 117 | * @param _jurisdiction Jurisdiction which need to be removed 118 | */ 119 | function removeFromJurisdictionList(bytes32 _jurisdiction) internal { 120 | for (uint i = 0; i < allowedJurisdictionsList.length; i++) { 121 | if (allowedJurisdictionsList[i] == _jurisdiction) 122 | allowedJurisdictionsList[i] = 0x0; 123 | } 124 | } 125 | 126 | 127 | /** 128 | * @dev remove the divisionJurisdiction from the blocked list of divisionJurisdiction 129 | * @param _blockedDivisionJurisdiction divisionJurisdiction which need to be removed 130 | */ 131 | function removeFromDivisionJurisdictionList(bytes32 _blockedDivisionJurisdiction) internal { 132 | for (uint i = 0; i < blockedDivisionJurisdictionsList.length; i++) { 133 | if (blockedDivisionJurisdictionsList[i] == _blockedDivisionJurisdiction) 134 | blockedDivisionJurisdictionsList[i] = 0x0; 135 | } 136 | } 137 | 138 | /** 139 | * @dev `addRoles` allows the adding of new roles to be added to whitelist 140 | * @param _allowedRoles User roles that can purchase the security 141 | */ 142 | function addRoles(uint8[] _allowedRoles) public { 143 | require(owner == msg.sender); 144 | require(!finalized); 145 | for (uint i = 0; i < _allowedRoles.length; ++i) { 146 | if(!allowedRoles[_allowedRoles[i]]) 147 | allowedRolesList.push(_allowedRoles[i]); 148 | 149 | allowedRoles[_allowedRoles[i]] = true; 150 | } 151 | } 152 | 153 | /** 154 | * @notice `updateDetails` 155 | * @param _details details of the template need to change 156 | * @return allowed boolean variable 157 | */ 158 | function updateDetails(bytes32 _details) public returns (bool allowed) { 159 | require(_details != 0x0); 160 | require(owner == msg.sender); 161 | bytes32 prevDetails = details; 162 | details = _details; 163 | DetailsUpdated(prevDetails, details, now); 164 | return true; 165 | } 166 | 167 | /** 168 | * @dev `finalizeTemplate` is used to finalize template.full compliance process/requirements 169 | * @return success 170 | */ 171 | function finalizeTemplate() public returns (bool success) { 172 | require(owner == msg.sender); 173 | require(removedJurisdictionsCount != allowedJurisdictionsList.length); 174 | require(allowedRolesList.length > 0); 175 | finalized = true; 176 | LogFinalizedTemplate(finalized, now); 177 | return true; 178 | } 179 | 180 | /** 181 | * @dev `checkTemplateRequirements` is a constant function that checks if templates requirements are met 182 | * @param _countryJurisdiction The ISO-3166 code of the investors country jurisdiction 183 | * @param _divisionJurisdiction The ISO-3166 code of the investors subdivision jurisdiction 184 | * @param _accredited Whether the investor is accredited or not 185 | * @param _role role of the user 186 | * @return allowed boolean variable 187 | */ 188 | function checkTemplateRequirements( 189 | bytes32 _countryJurisdiction, 190 | bytes32 _divisionJurisdiction, 191 | bool _accredited, 192 | uint8 _role 193 | ) public view returns (bool allowed) 194 | { 195 | require(_countryJurisdiction != 0x0); 196 | require(allowedJurisdictions[_countryJurisdiction] || !blockedDivisionJurisdictions[_divisionJurisdiction]); 197 | require(allowedRoles[_role]); 198 | if (accredited) { 199 | require(_accredited); 200 | } 201 | return true; 202 | } 203 | 204 | /** 205 | * @dev getTemplateDetails is a constant function that gets template details 206 | * @return bytes32 details, bool finalized 207 | */ 208 | function getTemplateDetails() view public returns (bytes32, bool) { 209 | require(expires > now); 210 | return (details, finalized); 211 | } 212 | 213 | /** 214 | * @dev `getUsageDetails` is a function to get all the details on template usage fees 215 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC 216 | */ 217 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, address) { 218 | return (fee, quorum, vestingPeriod, owner, KYC); 219 | } 220 | 221 | /** 222 | * @dev Get the list of allowed jurisdictions 223 | * @return bytes32[] 224 | */ 225 | function getAllowedJurisdictionsList() view public returns (bytes32[]) { 226 | return allowedJurisdictionsList; 227 | } 228 | 229 | /** 230 | * @dev Get the list of allowed roles 231 | * @return uin8[] 232 | */ 233 | function getAllowedRolesList() view public returns (uint8[]) { 234 | return allowedRolesList; 235 | } 236 | 237 | /** 238 | * @dev Get the list of allowed roles 239 | * @return bytes32[] 240 | */ 241 | function getblockedDivisionJurisdictionsList() view public returns (bytes32[]) { 242 | return blockedDivisionJurisdictionsList; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /contracts/Test Contracts/SecurityTokenMOCK.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../SecurityToken.sol'; 4 | 5 | /** 6 | * @title SecurityToken 7 | * @dev Contract (A Blueprint) that contains the functionalities of the security token 8 | */ 9 | 10 | contract SecurityTokenMOCK is SecurityToken { 11 | 12 | function SecurityTokenMOCK( 13 | string _name, 14 | string _ticker, 15 | uint256 _totalSupply, 16 | uint8 _decimals, 17 | address _owner, 18 | address _polyTokenAddress, 19 | address _polyCustomersAddress, 20 | address _polyComplianceAddress 21 | ) public 22 | SecurityToken( 23 | _name, 24 | _ticker, 25 | _totalSupply, 26 | _decimals, 27 | _owner, 28 | _polyTokenAddress, 29 | _polyCustomersAddress, 30 | _polyComplianceAddress 31 | 32 | ) 33 | {} 34 | 35 | function issueSecurityTokens(address _contributor, uint256 _amountOfSecurityTokens, uint256 _polyContributed) public onlyOffering returns (bool success) { 36 | 37 | // Update ST balances (transfers ST from STO to _contributor) 38 | balances[offering] = balances[offering].sub(_amountOfSecurityTokens); 39 | balances[_contributor] = balances[_contributor].add(_amountOfSecurityTokens); 40 | // ERC20 Transfer event 41 | Transfer(offering, _contributor, _amountOfSecurityTokens); 42 | // Update the amount of POLY a contributor has contributed and allocated to the owner 43 | contributedToSTO[_contributor] = contributedToSTO[_contributor].add(_polyContributed); 44 | allocations[owner].amount = allocations[owner].amount.add(_polyContributed); 45 | totalAllocated = totalAllocated.add(_polyContributed); 46 | LogTokenIssued(_contributor, _amountOfSecurityTokens, _polyContributed, now); 47 | return true; 48 | } 49 | 50 | /** 51 | * @dev Start the offering by sending all the tokens to STO contract 52 | * @return bool 53 | */ 54 | function initialiseOffering(address _offering) onlyOwner external returns (bool success) { 55 | require(offering == 0x0); 56 | offering = _offering; 57 | shareholders[offering] = Shareholder(this, true, 5); 58 | uint256 tokenAmount = this.balanceOf(msg.sender); 59 | require(tokenAmount == totalSupply); 60 | balances[offering] = balances[offering].add(tokenAmount); 61 | balances[msg.sender] = balances[msg.sender].sub(tokenAmount); 62 | Transfer(owner, offering, tokenAmount); 63 | return true; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /contracts/interfaces/ICompliance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /* 4 | Polymath compliance protocol is intended to ensure regulatory compliance 5 | in the jurisdictions that security tokens are being offered in. The compliance 6 | protocol allows security tokens remain interoperable so that anyone can 7 | build on top of the Polymath platform and extend it's functionality. 8 | */ 9 | 10 | interface ICompliance { 11 | 12 | /** 13 | * @dev `setRegistrarAddress` This function set the SecurityTokenRegistrar contract address. 14 | * @param _STRegistrar It is the `this` reference of STR contract 15 | * @return bool 16 | */ 17 | 18 | function setRegistrarAddress(address _STRegistrar) public returns (bool); 19 | 20 | /** 21 | * @dev `createTemplate` is a simple function to create a new compliance template 22 | * @param _offeringType The name of the security being issued 23 | * @param _issuerJurisdiction The jurisdiction id of the issuer 24 | * @param _accredited Accreditation status required for investors 25 | * @param _KYC KYC provider used by the template 26 | * @param _details Details of the offering requirements 27 | * @param _expires Timestamp of when the template will expire 28 | * @param _fee Amount of POLY to use the template (held in escrow until issuance) 29 | * @param _quorum Minimum percent of shareholders which need to vote to freeze 30 | * @param _vestingPeriod Length of time to vest funds 31 | */ 32 | function createTemplate( 33 | string _offeringType, 34 | bytes32 _issuerJurisdiction, 35 | bool _accredited, 36 | address _KYC, 37 | bytes32 _details, 38 | uint256 _expires, 39 | uint256 _fee, 40 | uint8 _quorum, 41 | uint256 _vestingPeriod 42 | ) public; 43 | 44 | /** 45 | * @dev Propose a bid for a security token issuance 46 | * @param _securityToken The security token being bid on 47 | * @param _template The unique template address 48 | * @return bool success 49 | */ 50 | function proposeTemplate( 51 | address _securityToken, 52 | address _template 53 | ) public returns (bool success); 54 | 55 | /** 56 | * @dev Propose a Security Token Offering Contract for an issuance 57 | * @param _securityToken The security token being bid on 58 | * @param _factoryAddress The security token offering contract address 59 | * @return bool success 60 | */ 61 | function proposeOfferingFactory( 62 | address _securityToken, 63 | address _factoryAddress 64 | ) public returns (bool success); 65 | 66 | /** 67 | * @dev Cancel a Template proposal if the bid hasn't been accepted 68 | * @param _securityToken The security token being bid on 69 | * @param _templateProposalIndex The template proposal array index 70 | * @return bool success 71 | */ 72 | function cancelTemplateProposal( 73 | address _securityToken, 74 | uint256 _templateProposalIndex 75 | ) public returns (bool success); 76 | 77 | /** 78 | * @dev Register the Offering factory by the developer. 79 | * @param _factoryAddress address of the offering factory 80 | * @return bool success 81 | */ 82 | function registerOfferingFactory ( 83 | address _factoryAddress 84 | ) public returns (bool success); 85 | 86 | /** 87 | * @dev Cancel a Offering factory proposal if the bid hasn't been accepted 88 | * @param _securityToken The security token being bid on 89 | * @param _offeringFactoryProposalIndex The offeringFactory proposal array index 90 | * @return bool success 91 | */ 92 | function cancelOfferingFactoryProposal( 93 | address _securityToken, 94 | uint256 _offeringFactoryProposalIndex 95 | ) public returns (bool success); 96 | 97 | /** 98 | * @dev `updateTemplateReputation` is a constant function that updates the 99 | history of a security token template usage to keep track of previous uses 100 | * @param _template The unique template address 101 | * @param _polyRaised Poly raised by template 102 | */ 103 | function updateTemplateReputation (address _template, uint256 _polyRaised) external returns (bool success); 104 | 105 | /** 106 | * @dev `updateOfferingReputation` is a constant function that updates the 107 | history of a security token offering contract to keep track of previous uses 108 | * @param _offeringFactory The smart contract address of the STO contract 109 | * @param _polyRaised Poly raised by template 110 | */ 111 | function updateOfferingFactoryReputation (address _offeringFactory, uint256 _polyRaised) external returns (bool success); 112 | 113 | /** 114 | * @dev Get template details by the proposal index 115 | * @param _securityTokenAddress The security token ethereum address 116 | * @param _templateIndex The array index of the template being checked 117 | * @return Template struct 118 | */ 119 | function getTemplateByProposal(address _securityTokenAddress, uint8 _templateIndex) view public returns ( 120 | address _template 121 | ); 122 | 123 | /** 124 | * @dev Get security token offering smart contract details by the proposal index 125 | * @param _securityTokenAddress The security token ethereum address 126 | * @param _offeringFactoryProposalIndex The array index of the STO contract being checked 127 | * @return Contract struct 128 | */ 129 | function getOfferingFactoryByProposal(address _securityTokenAddress, uint8 _offeringFactoryProposalIndex) view public returns ( 130 | address _offeringFactoryAddress 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /contracts/interfaces/ICustomers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ICustomers { 4 | 5 | /** 6 | * @dev Allow new provider applications 7 | * @param _name The provider's name 8 | * @param _details A SHA256 hash of the new providers details 9 | * @param _fee The fee charged for customer verification 10 | */ 11 | function newProvider(string _name, bytes32 _details, uint256 _fee) public returns (bool success); 12 | 13 | /** 14 | * @dev Change a providers fee 15 | * @param _newFee The new fee of the provider 16 | */ 17 | function changeFee(uint256 _newFee) public returns (bool success); 18 | 19 | /** 20 | * @dev Verify an investor 21 | * @param _customer The customer's public key address 22 | * @param _countryJurisdiction The country urisdiction code of the customer 23 | * @param _divisionJurisdiction The subdivision jurisdiction code of the customer 24 | * @param _role The type of customer - investor:1, delegate:2, issuer:3, marketmaker:4, etc. 25 | * @param _accredited Whether the customer is accredited or not (only applied to investors) 26 | * @param _expires The time the verification expires 27 | */ 28 | function verifyCustomer( 29 | address _customer, 30 | bytes32 _countryJurisdiction, 31 | bytes32 _divisionJurisdiction, 32 | uint8 _role, 33 | bool _accredited, 34 | uint256 _expires, 35 | uint _nonce, 36 | uint8 _v, 37 | bytes32 _r, 38 | bytes32 _s 39 | ) public returns (bool success); 40 | 41 | /////////////////// 42 | /// GET Functions 43 | ////////////////// 44 | 45 | /** 46 | * @dev Get customer attestation data by KYC provider and customer ethereum address 47 | * @param _provider Address of the KYC provider. 48 | * @param _customer Address of the customer ethereum address 49 | */ 50 | function getCustomer(address _provider, address _customer) public view returns ( 51 | bytes32, 52 | bytes32, 53 | bool, 54 | uint8, 55 | uint256 56 | ); 57 | 58 | /** 59 | * Get provider details and fee by ethereum address 60 | * @param _providerAddress Address of the KYC provider 61 | */ 62 | function getProvider(address _providerAddress) public view returns ( 63 | string name, 64 | uint256 joined, 65 | bytes32 details, 66 | uint256 fee 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /// ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) 4 | interface IERC20 { 5 | function balanceOf(address _owner) public view returns (uint256 balance); 6 | function transfer(address _to, uint256 _value) public returns (bool success); 7 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 8 | function approve(address _spender, uint256 _value) public returns (bool success); 9 | function allowance(address _owner, address _spender) public view returns (uint256 remaining); 10 | function totalSupply() public view returns (uint256); 11 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 12 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interfaces/IOfferingFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface IOfferingFactory { 4 | 5 | /** 6 | * @dev It facilitate the creation of the STO contract with essentials parameters 7 | * @param _startTime Unix timestamp to start the offering 8 | * @param _endTime Unix timestamp to end the offering 9 | * @param _polyTokenRate Price of one security token in terms of poly 10 | * @param _maxPoly Maximum amount of poly issuer wants to collect 11 | * @return address Address of the new offering instance 12 | */ 13 | function createOffering( 14 | uint256 _startTime, 15 | uint256 _endTime, 16 | uint256 _polyTokenRate, 17 | uint256 _maxPoly 18 | ) public returns (address); 19 | 20 | /** 21 | * @dev `getUsageDetails` is a function to get all the details on factory usage fees 22 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, string description 23 | */ 24 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, bytes32); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /contracts/interfaces/ISecurityToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ISecurityToken { 4 | 5 | /** 6 | * @dev `selectTemplate` Select a proposed template for the issuance 7 | * @param _templateIndex Array index of the delegates proposed template 8 | * @return bool success 9 | */ 10 | function selectTemplate(uint8 _templateIndex) public returns (bool success); 11 | 12 | /** 13 | * @dev Update compliance proof hash for the issuance 14 | * @param _newMerkleRoot New merkle root hash of the compliance Proofs 15 | * @param _complianceProof Compliance Proof hash 16 | * @return bool success 17 | */ 18 | function updateComplianceProof( 19 | bytes32 _newMerkleRoot, 20 | bytes32 _complianceProof 21 | ) public returns (bool success); 22 | 23 | /** 24 | * @dev `selectOfferingFactory` Select an security token offering proposal for the issuance 25 | * @param _offeringFactoryProposalIndex Array index of the STO proposal 26 | * @return bool success 27 | */ 28 | function selectOfferingFactory ( 29 | uint8 _offeringFactoryProposalIndex 30 | ) public returns (bool success); 31 | 32 | /** 33 | * @dev Start the offering by sending all the tokens to STO contract 34 | * @param _startTime Unix timestamp to start the offering 35 | * @param _endTime Unix timestamp to end the offering 36 | * @param _polyTokenRate Price of one security token in terms of poly 37 | * @param _maxPoly Maximum amount of poly issuer wants to collect 38 | * @param _lockupPeriod Length of time raised POLY will be locked up for dispute 39 | * @param _quorum Percent of initial investors required to freeze POLY raise 40 | * @return bool 41 | */ 42 | function initialiseOffering(uint256 _startTime, uint256 _endTime, uint256 _polyTokenRate, uint256 _maxPoly, uint256 _lockupPeriod, uint8 _quorum) external returns (bool success); 43 | 44 | /** 45 | * @dev Add a verified address to the Security Token whitelist 46 | * @param _whitelistAddress Address attempting to join ST whitelist 47 | * @return bool success 48 | */ 49 | function addToWhitelist(address _whitelistAddress) public returns (bool success); 50 | 51 | /** 52 | * @dev Add verified addresses to the Security Token whitelist 53 | * @param _whitelistAddresses Array of addresses attempting to join ST whitelist 54 | * @return bool success 55 | */ 56 | function addToWhitelistMulti(address[] _whitelistAddresses) public returns (bool success); 57 | 58 | /** 59 | * @dev Removes a previosly verified address to the Security Token blacklist 60 | * @param _blacklistAddress Address being added to the blacklist 61 | * @return bool success 62 | */ 63 | function addToBlacklist(address _blacklistAddress) public returns (bool success); 64 | 65 | /** 66 | * @dev Removes previously verified addresseses to the Security Token whitelist 67 | * @param _blacklistAddresses Array of addresses attempting to join ST whitelist 68 | * @return bool success 69 | */ 70 | function addToBlacklistMulti(address[] _blacklistAddresses) public returns (bool success); 71 | 72 | /** 73 | * @dev Allow POLY allocations to be withdrawn by owner, delegate, and the STO auditor at appropriate times 74 | * @return bool success 75 | */ 76 | function withdrawPoly() public returns (bool success); 77 | 78 | /** 79 | * @dev Vote to freeze the fee of a certain network participant 80 | * @param _recipient The fee recipient being protested 81 | * @return bool success 82 | */ 83 | function voteToFreeze(address _recipient) public returns (bool success); 84 | 85 | /** 86 | * @dev `issueSecurityTokens` is used by the STO to keep track of STO investors 87 | * @param _contributor The address of the person whose contributing 88 | * @param _amountOfSecurityTokens The amount of ST to pay out. 89 | * @param _polyContributed The amount of POLY paid for the security tokens. 90 | */ 91 | function issueSecurityTokens(address _contributor, uint256 _amountOfSecurityTokens, uint256 _polyContributed) public returns (bool success); 92 | 93 | /// Get token details 94 | function getTokenDetails() view public returns (address, address, bytes32, address, address, address); 95 | 96 | /// Get token decimals 97 | function decimals() view public returns (uint8); 98 | } 99 | -------------------------------------------------------------------------------- /contracts/interfaces/ISecurityTokenRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ISecurityTokenRegistrar { 4 | 5 | /** 6 | * @dev Creates a new Security Token and saves it to the registry 7 | * @param _nameSpace Name space for this security token 8 | * @param _name Name of the security token 9 | * @param _ticker Ticker name of the security 10 | * @param _totalSupply Total amount of tokens being created 11 | * @param _owner Ethereum public key address of the security token owner 12 | * @param _type Type of security being tokenized 13 | */ 14 | function createSecurityToken ( 15 | string _nameSpace, 16 | string _name, 17 | string _ticker, 18 | uint256 _totalSupply, 19 | uint8 _decimals, 20 | address _owner, 21 | uint8 _type 22 | ) external; 23 | 24 | /** 25 | * @dev Get Security token details by its ethereum address 26 | * @param _STAddress Security token address 27 | */ 28 | function getSecurityTokenData(address _STAddress) public view returns ( 29 | string, 30 | string, 31 | address, 32 | uint8 33 | ); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /contracts/interfaces/ITemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ITemplate { 4 | 5 | /** 6 | * @dev `addJurisdiction` allows the adding of new jurisdictions to a template 7 | * @param _allowedJurisdictions An array of jurisdictions 8 | * @param _allowed An array of whether the jurisdiction is allowed to purchase the security or not 9 | */ 10 | function addJurisdiction(bytes32[] _allowedJurisdictions, bool[] _allowed) public; 11 | 12 | /** 13 | * @dev `addDivisionJurisdiction` allows the adding of new jurisdictions to a template 14 | * @param _blockedDivisionJurisdictions An array of jurisdictions 15 | * @param _blocked An array of whether the jurisdiction is allowed to purchase the security or not 16 | */ 17 | function addDivisionJurisdiction(bytes32[] _blockedDivisionJurisdictions, bool[] _blocked) public; 18 | 19 | /** 20 | * @dev `addRole` allows the adding of new roles to be added to whitelist 21 | * @param _allowedRoles User roles that can purchase the security 22 | */ 23 | function addRoles(uint8[] _allowedRoles) public; 24 | 25 | /** 26 | * @notice `updateDetails` 27 | * @param _details details of the template need to change 28 | * @return allowed boolean variable 29 | */ 30 | function updateDetails(bytes32 _details) public returns (bool allowed); 31 | 32 | /** 33 | * @dev `finalizeTemplate` is used to finalize template.full compliance process/requirements 34 | * @return success 35 | */ 36 | function finalizeTemplate() public returns (bool success); 37 | 38 | /** 39 | * @dev `checkTemplateRequirements` is a constant function that checks if templates requirements are met 40 | * @param _countryJurisdiction The ISO-3166 code of the investors country jurisdiction 41 | * @param _divisionJurisdiction The ISO-3166 code of the investors subdivision jurisdiction 42 | * @param _accredited Whether the investor is accredited or not 43 | * @param _role role of the user 44 | * @return allowed boolean variable 45 | */ 46 | function checkTemplateRequirements( 47 | bytes32 _countryJurisdiction, 48 | bytes32 _divisionJurisdiction, 49 | bool _accredited, 50 | uint8 _role 51 | ) public view returns (bool allowed); 52 | 53 | /** 54 | * @dev getTemplateDetails is a constant function that gets template details 55 | * @return bytes32 details, bool finalized 56 | */ 57 | function getTemplateDetails() view public returns (bytes32, bool); 58 | 59 | /** 60 | * @dev `getUsageDetails` is a function to get all the details on template usage fees 61 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC 62 | */ 63 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, address); 64 | 65 | /** 66 | * @dev Get the list of allowed jurisdictions 67 | * @return bytes32[] 68 | */ 69 | function getAllowedJurisdictionsList() view public returns (bytes32[]); 70 | 71 | /** 72 | * @dev Get the list of allowed roles 73 | * @return uin8[] 74 | */ 75 | function getAllowedRolesList() view public returns (uint8[]); 76 | 77 | /** 78 | * @dev Get the list of allowed roles 79 | * @return bytes32[] 80 | */ 81 | function getblockedDivisionJurisdictionsList() view public returns (bytes32[]); 82 | } 83 | -------------------------------------------------------------------------------- /docs/INVESTORS.md: -------------------------------------------------------------------------------- 1 | # Polymath Investors 2 | 3 | Polymath investors provide capital to Security Token issuers on the Polymath 4 | network. This is done by contributing POLY or ETH to Security Token Offering 5 | contracts, or through secondary markets (exchanges, over the counter, etc.). 6 | 7 | ## Why invest in Polymath Security Tokens? 8 | 9 | Security tokens allow you to invest in real world assets, i.e. equities, debt, 10 | derivatives, etc. Unlike utility tokens, which are only used to gain access to a 11 | decentralized platform or protocol, security tokens can be issued and purchased 12 | explicitly for investment purposes. 13 | 14 | ## How do I invest in Security Tokens on Polymath? 15 | 16 | 1. In order to invest a security tokens, you will need to meet the requirements 17 | the security token regulations you wish to invest in. So the first step is to 18 | find the security token offering you are interested in. With the contract 19 | address you can obtain the regulatory template details via the 20 | `SecurityToken.getTokenDetails()` function. 21 | 22 | 2. Using the template address, you can now call 23 | `Template.checkTemplateRequirements()` with your jurisdiction and 24 | accreditation status to determine if you are be eligible to invest in the 25 | security token. 26 | 27 | 3. If you are eligible to own the security token, you should begin the KYC 28 | process by going to the KYC provider onboarding page and uploading the 29 | required identity and accreditation documents. The KYC provider address was 30 | provided in step 2, and additional details can be obtained via the 31 | `Customers.getProvider()` function. 32 | 33 | 4. After the KYC provider has verified your identity and accreditation status, 34 | your ethereum address will be added to the security token whitelist. Allowing 35 | you to participate in both the initial Security Token Offering (STO), or 36 | trade the token to other verified investors through secondary markets. 37 | 38 | For example, if you are investing in Alice Inc. security tokens which are issued 39 | out of the USA as a 506 (b) offering and specifies a KYC provider as Bob KYC 40 | Inc. (which checks accreditation status), you as an investor will need to visit 41 | bobskyc.com and upload your proof of identity/accreditation status. If 42 | successful, bobskyc.com will add you to the Security token whitelist, and you 43 | will be able to participate in the Security Token offering or purchase it on 44 | secondary markets. 45 | -------------------------------------------------------------------------------- /docs/ISSUERS.md: -------------------------------------------------------------------------------- 1 | # Polymath Issuers 2 | 3 | Polymath issuers raise funds through Polymath via Security Token Offerings. They 4 | do this by tokenizing their assets, i.e. company/hedge fund/trust equity, or 5 | debt instruments. 6 | 7 | ## How do I issue a security token? 8 | 9 | 1. In order to issue a security token, you should first visit a security token 10 | creation wizard which helps match issuers with delegates and developers to 11 | create a fully compliant security token offering. 12 | 13 | 2. Once you find a security token creation host, you will enter the details of 14 | your offering. The required details are token name, total supply, ticker 15 | name, percent raised in POLY, issuing jurisdiction, your ethereum address, 16 | and potentially many other fields (depending on what the host deems 17 | necessary). Only the fields mentioned above are required to create the 18 | security token on-chain. 19 | 20 | 3. Depending on the host, you may be required to pre-authorize a payment in 21 | POLY, for the services offered by the host. This can be done via the 22 | `PolyToken.approve()` function. 23 | 24 | 4. The host will review details, and will call 25 | `SecurityTokenRegistrar.createSecurityToken()` with the details of the 26 | Security Token. Note: if a fee is required by the host and the 27 | pre-authorization was not made by the issuer in step 3, this transaction will 28 | fail. 29 | 30 | 5. The host will provide you with the result of the creation and forward the 31 | issuance details along to the delegate and developer network to begin 32 | making proposals for the issuance. The security tokens are created and 33 | transferred to the Ethereum address provided in step 2. At this stage, the 34 | tokens will be non-transferrable. 35 | 36 | 6. Delegates will make proposals of different compliance templates that can be 37 | applied to the security token. These can be listened to via the 38 | `Compliance.LogNewTemplateProposal` event. Developer proposals can be done 39 | similarly through `Compliance.LogNewDeveloperProposal`. 40 | 41 | 7. After reviewing proposals, the issuer can select a template proposal via the 42 | `SecurityToken.selectTemplate()` function and a developer proposal via the 43 | `SecurityToken.selectOfferingProposal()` function. At this point investors 44 | are now eligible to begin the KYC process (specified by the template) and the 45 | issuer is eligible to transfer the Security Token to the STO contract 46 | (specified by the offering proposal). 47 | 48 | 8. Once the offering start time begins, the raised funds will begin transferring 49 | to the issuers address (in accordance with the STO contract spec). 50 | -------------------------------------------------------------------------------- /docs/KYCProviders.md: -------------------------------------------------------------------------------- 1 | # Polymath KYC Providers 2 | 3 | Polymath KYC providers attest customer claims such as identity, accreditation 4 | status, customer type (investor, issuer, exchange, etc), and potentially other 5 | information. A KYC provider could use manual methods, an 3rd party verification 6 | API, or a mix of both. 7 | 8 | KYC providers are an essential role in the Polymath ecosystem and gain 9 | reputation extrinsically by being reused on different compliance templates. 10 | 11 | ## How do I verify an investor? 12 | 13 | 1. Become a KYC provider by calling `Customers.newProvider()` and pay the 14 | required fee to join as a KYC provider (to prevent spam). A KYC provider can 15 | create multiple providers to represent different types of verification 16 | 'packages' for different fees. 17 | 18 | 2. Investor uploads their identity documents to your API or manually with an 19 | Ethereum address. 20 | 21 | 3. You run KYC verification, accreditation checks or whatever the attestation 22 | requirements of your package offers and make a call to the 23 | `Customers.verifyCustomer()` function. 24 | 25 | 4. (This part can be done by anyone) The investor/issuer calls 26 | `SecurityToken.addToWhitelist()` which checks the KYC provider address 27 | customer datastore in Customers.sol and verifies the customer verifications 28 | meet the security token compliance template requirements. 29 | 30 | For example, if the Government of Zimbabwe wants to issue a universal income 31 | token which has voting rights to it's citizens. They could create a new KYC 32 | provider address and require each citizen to visit their office with documents 33 | and an ethereum address. Once verified, they would make the function call to add 34 | them to their datastore in Customers.sol and call addToWhitelist in the Zimbabwe 35 | Universal Income security token. Additionally, other issuers looking to 36 | issue security tokens to only Zimbawe citizens in the future, as long as the 37 | investor verifications haven't expired (specified by the KYC provider when 38 | adding them to the datastore), they can be added to the new security token 39 | whitelist without having to re-verify. 40 | -------------------------------------------------------------------------------- /docs/Roles.md: -------------------------------------------------------------------------------- 1 | ## What is Role ? 2 | 3 | Polymath platform user identify according to the roles it possesed. 4 | Role is an unique integer which is used to identify the actor for what it 5 | authorized at Polymath platform. 6 | 7 | ## Types of Roles 8 | 9 | Polymath platfrom have 4 types of different roles for different actors 10 | 11 | | Role | Actor | 12 | |------|-------| 13 | | 1 | Investor| 14 | | 2 | Delegate | 15 | | 3 | Issuer | 16 | | 4 | MarketMaker | 17 | | 5 | Offering Contract | 18 | 19 | ## Actor Definition 20 | __Investor__ : Invest their capital in the security token offering at Polymath Platform. 21 | 22 | __Issuer__ : Raise funds through Polymath via Security Token Offerings. 23 | 24 | __Delegate__ : Authorized persons or organisations to represent the issuers. 25 | 26 | __MarketMaker__ : Liquidity provider. 27 | 28 | __Offering Contract__ : Contract to facilitate the distribution of Security Token 29 | 30 | ## Where Roles used in Polymath-core 31 | `Template.addRoles` used to adding roles into `Template.allowedRoles` mapping. 32 | Only allowed roles can participate in the security token offering. 33 | -------------------------------------------------------------------------------- /docs/TEMPLATES.md: -------------------------------------------------------------------------------- 1 | # Polymath ST20 Templates 2 | 3 | In order to streamline the process of issuing a compliant security token, and 4 | leveraging the decentralized and open nature of blockchains, the concept of a 5 | security token compliance template is used. 6 | 7 | Compliance templates allow issuers to easily apply transfer restrictions and a 8 | KYC provider to ST20 tokens. The main function of templates are to specify the 9 | jurisdictions and accreditation status that the investor must be set to (by a 10 | the KYC provider on the template) to hold the security token. If they do not 11 | meet the template requirements, any transfer of security tokens to their address 12 | will fail. They can also store a hash of documents related to the offering 13 | requirements on chain. 14 | 15 | ## Who creates templates? 16 | 17 | Delegates will create templates, and it is likely that the community will steer 18 | towards a few templates based off recommendations by industry leaders. It is 19 | also possible that individual hosts create their own templates that fit within 20 | their existing processes. 21 | 22 | ## What is in a template? 23 | 24 | owner - The delegate who is claiming the template meets regulatory requirements 25 | by creating the template 26 | 27 | offeringType - A string description i.e. 506b 28 | 29 | issuerJurisdiction - The issuers ISO3166 jurisdiction 30 | 31 | allowedJurisdiction - A mapping of allowed ISO3166 jurisdictions 32 | 33 | allowedRoles - A mapping of allowed roles (uint) 34 | 35 | accredited - Whether or not the investor must be accredited 36 | 37 | KYC - The KYC providers address 38 | 39 | details - A hash of compliance documents for the security offering 40 | 41 | finalized - Allows the owner to make edits to the template and finalize it when 42 | ready 43 | 44 | expires - When the template expires and can no longer be applied to security 45 | tokens 46 | 47 | fee - The fee the delegate would like for their services 48 | 49 | quorum - The percent of initial investors in the security token to freeze 50 | service fee 51 | 52 | vestingPeriod - How long the delegate is willing to lockup their service fees 53 | -------------------------------------------------------------------------------- /docs/Types.md: -------------------------------------------------------------------------------- 1 | # Security Token Types 2 | Polymath platform entertain three types of securities to be tokenized 3 | i.e. __Units__, __Debts__, __Equity__. In future number may increase. 4 | 5 | ## Types Description 6 | __Equity__: An equity security represents ownership interest held by shareholders in an entity 7 | (a company, partnership or trust), realized in the form of shares of capital stock, which includes 8 | shares of both common and preferred stock. Holders of equity securities are typically not entitled 9 | to regular payments (though equity securities often do pay out dividends), but they are able to profit 10 | from capital gains when they sell the securities (assuming they've increased in value, naturally). 11 | 12 | __Debts__: A debt security represents money that is borrowed and must be repaid, with terms that 13 | stipulates the size of the loan, interest rate and maturity or renewal date. 14 | 15 | __Units__: In Trust / Limited partnership - Holders owns stake in trust, having right on the income generated by the trust / partnership, partnership could own multiple investments. 16 | 17 | ## Types definition in Polymath-core 18 | Types are mapped by the `uint8` integer in polymath-core. 19 | 20 | |Types|Integer Mapped| 21 | |------|--------------| 22 | |1|Equity| 23 | |2|Debts| 24 | |3|Units| -------------------------------------------------------------------------------- /flat/Customers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /// ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) 4 | interface IERC20 { 5 | function balanceOf(address _owner) public view returns (uint256 balance); 6 | function transfer(address _to, uint256 _value) public returns (bool success); 7 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 8 | function approve(address _spender, uint256 _value) public returns (bool success); 9 | function allowance(address _owner, address _spender) public view returns (uint256 remaining); 10 | function totalSupply() public view returns (uint256); 11 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 12 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 13 | } 14 | 15 | interface ICustomers { 16 | 17 | /** 18 | * @dev Allow new provider applications 19 | * @param _name The provider's name 20 | * @param _details A SHA256 hash of the new providers details 21 | * @param _fee The fee charged for customer verification 22 | */ 23 | function newProvider(string _name, bytes32 _details, uint256 _fee) public returns (bool success); 24 | 25 | /** 26 | * @dev Change a providers fee 27 | * @param _newFee The new fee of the provider 28 | */ 29 | function changeFee(uint256 _newFee) public returns (bool success); 30 | 31 | /** 32 | * @dev Verify an investor 33 | * @param _customer The customer's public key address 34 | * @param _countryJurisdiction The country urisdiction code of the customer 35 | * @param _divisionJurisdiction The subdivision jurisdiction code of the customer 36 | * @param _role The type of customer - investor:1, delegate:2, issuer:3, marketmaker:4, etc. 37 | * @param _accredited Whether the customer is accredited or not (only applied to investors) 38 | * @param _expires The time the verification expires 39 | */ 40 | function verifyCustomer( 41 | address _customer, 42 | bytes32 _countryJurisdiction, 43 | bytes32 _divisionJurisdiction, 44 | uint8 _role, 45 | bool _accredited, 46 | uint256 _expires, 47 | uint _nonce, 48 | uint8 _v, 49 | bytes32 _r, 50 | bytes32 _s 51 | ) public returns (bool success); 52 | 53 | /////////////////// 54 | /// GET Functions 55 | ////////////////// 56 | 57 | /** 58 | * @dev Get customer attestation data by KYC provider and customer ethereum address 59 | * @param _provider Address of the KYC provider. 60 | * @param _customer Address of the customer ethereum address 61 | */ 62 | function getCustomer(address _provider, address _customer) public view returns ( 63 | bytes32, 64 | bytes32, 65 | bool, 66 | uint8, 67 | uint256 68 | ); 69 | 70 | /** 71 | * Get provider details and fee by ethereum address 72 | * @param _providerAddress Address of the KYC provider 73 | */ 74 | function getProvider(address _providerAddress) public view returns ( 75 | string name, 76 | uint256 joined, 77 | bytes32 details, 78 | uint256 fee 79 | ); 80 | } 81 | 82 | /* 83 | Polymath customer registry is used to ensure regulatory compliance 84 | of the investors, provider, and issuers. The customers registry is a central 85 | place where ethereum addresses can be whitelisted to purchase certain security 86 | tokens based on their verifications by providers. 87 | */ 88 | 89 | 90 | 91 | 92 | /** 93 | * @title Customers 94 | * @dev Contract use to register the user on the Polymath platform 95 | */ 96 | 97 | contract Customers is ICustomers { 98 | 99 | string public VERSION = "2"; 100 | 101 | IERC20 POLY; // Instance of the POLY token 102 | 103 | struct Customer { // Structure use to store the details of the customers 104 | bytes32 countryJurisdiction; // Customers country jurisdiction as ex - ISO3166 105 | bytes32 divisionJurisdiction; // Customers sub-division jurisdiction as ex - ISO3166 106 | uint256 joined; // Timestamp when customer register 107 | uint8 role; // Role of the customer 108 | bool accredited; // Accrediation status of the customer 109 | bytes32 proof; // Proof for customer 110 | uint256 expires; // Timestamp when customer verification expires 111 | } 112 | 113 | mapping(address => mapping(address => Customer)) public customers; // Customers (kyc provider address => customer address) 114 | mapping(address => mapping(uint256 => bool)) public nonceMap; // Map of used nonces by customer 115 | 116 | struct Provider { // KYC/Accreditation Provider 117 | string name; // Name of the provider 118 | uint256 joined; // Timestamp when provider register 119 | bytes32 details; // Details of provider 120 | uint256 fee; // Fee charged by the KYC providers 121 | } 122 | 123 | mapping(address => Provider) public providers; // KYC/Accreditation Providers 124 | 125 | // Notifications 126 | event LogNewProvider(address indexed providerAddress, string name, bytes32 details, uint256 _fee); 127 | event LogCustomerVerified(address indexed customer, address indexed provider, uint8 role); 128 | 129 | // Modifier 130 | modifier onlyProvider() { 131 | require(providers[msg.sender].details != 0x0); 132 | _; 133 | } 134 | 135 | /** 136 | * @dev Constructor 137 | */ 138 | function Customers(address _polyTokenAddress) public { 139 | POLY = IERC20(_polyTokenAddress); 140 | } 141 | 142 | /** 143 | * @dev Allow new provider applications 144 | * @param _name The provider's name 145 | * @param _details A SHA256 hash of the new providers details 146 | * @param _fee The fee charged for customer verification 147 | */ 148 | function newProvider(string _name, bytes32 _details, uint256 _fee) public returns (bool success) { 149 | require(_details != 0x0); 150 | require(providers[msg.sender].details == 0x0); 151 | providers[msg.sender] = Provider(_name, now, _details, _fee); 152 | LogNewProvider(msg.sender, _name, _details, _fee); 153 | return true; 154 | } 155 | 156 | /** 157 | * @dev Change a providers fee 158 | * @param _newFee The new fee of the provider 159 | */ 160 | function changeFee(uint256 _newFee) onlyProvider public returns (bool success) { 161 | providers[msg.sender].fee = _newFee; 162 | return true; 163 | } 164 | 165 | 166 | /** 167 | * @dev Verify an investor 168 | * @param _customer The customer's public key address 169 | * @param _countryJurisdiction The jurisdiction country code of the customer 170 | * @param _divisionJurisdiction The jurisdiction subdivision code of the customer 171 | * @param _role The type of customer - investor:1, delegate:2, issuer:3, marketmaker:4, etc. 172 | * @param _accredited Whether the customer is accredited or not (only applied to investors) 173 | * @param _expires The time the verification expires 174 | * @param _nonce nonce of signature (avoid replay attack) 175 | * @param _v customer signature 176 | * @param _r customer signature 177 | * @param _s customer signature 178 | */ 179 | function verifyCustomer( 180 | address _customer, 181 | bytes32 _countryJurisdiction, 182 | bytes32 _divisionJurisdiction, 183 | uint8 _role, 184 | bool _accredited, 185 | uint256 _expires, 186 | uint _nonce, 187 | uint8 _v, 188 | bytes32 _r, 189 | bytes32 _s 190 | ) public onlyProvider returns (bool success) 191 | { 192 | require(_expires > now); 193 | require(nonceMap[_customer][_nonce] == false); 194 | nonceMap[_customer][_nonce] = true; 195 | bytes32 hash = keccak256(this, msg.sender, _countryJurisdiction, _divisionJurisdiction, _role, _accredited, _nonce); 196 | require(ecrecover(keccak256("\x19Ethereum Signed Message:\n32", hash), _v, _r, _s) == _customer); 197 | require(POLY.transferFrom(_customer, msg.sender, providers[msg.sender].fee)); 198 | customers[msg.sender][_customer].countryJurisdiction = _countryJurisdiction; 199 | customers[msg.sender][_customer].divisionJurisdiction = _divisionJurisdiction; 200 | customers[msg.sender][_customer].role = _role; 201 | customers[msg.sender][_customer].accredited = _accredited; 202 | customers[msg.sender][_customer].expires = _expires; 203 | LogCustomerVerified(_customer, msg.sender, _role); 204 | return true; 205 | } 206 | 207 | /////////////////// 208 | /// GET Functions 209 | ////////////////// 210 | 211 | /** 212 | * @dev Get customer attestation data by KYC provider and customer ethereum address 213 | * @param _provider Address of the KYC provider. 214 | * @param _customer Address of the customer ethereum address 215 | */ 216 | function getCustomer(address _provider, address _customer) public view returns ( 217 | bytes32, 218 | bytes32, 219 | bool, 220 | uint8, 221 | uint256 222 | ) { 223 | return ( 224 | customers[_provider][_customer].countryJurisdiction, 225 | customers[_provider][_customer].divisionJurisdiction, 226 | customers[_provider][_customer].accredited, 227 | customers[_provider][_customer].role, 228 | customers[_provider][_customer].expires 229 | ); 230 | } 231 | 232 | /** 233 | * Get provider details and fee by ethereum address 234 | * @param _providerAddress Address of the KYC provider 235 | */ 236 | function getProvider(address _providerAddress) public view returns ( 237 | string name, 238 | uint256 joined, 239 | bytes32 details, 240 | uint256 fee 241 | ) { 242 | return ( 243 | providers[_providerAddress].name, 244 | providers[_providerAddress].joined, 245 | providers[_providerAddress].details, 246 | providers[_providerAddress].fee 247 | ); 248 | } 249 | 250 | } -------------------------------------------------------------------------------- /flat/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | require (msg.sender == owner); 9 | _; 10 | } 11 | 12 | function Migrations() public { 13 | owner = msg.sender; 14 | } 15 | 16 | function setCompleted(uint completed)public restricted { 17 | last_completed_migration = completed; 18 | } 19 | 20 | function upgrade(address new_address)public restricted { 21 | Migrations upgraded = Migrations(new_address); 22 | upgraded.setCompleted(last_completed_migration); 23 | } 24 | } -------------------------------------------------------------------------------- /flat/OfferingFactories/SimpleCappedOffering.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ISecurityToken { 4 | 5 | /** 6 | * @dev `selectTemplate` Select a proposed template for the issuance 7 | * @param _templateIndex Array index of the delegates proposed template 8 | * @return bool success 9 | */ 10 | function selectTemplate(uint8 _templateIndex) public returns (bool success); 11 | 12 | /** 13 | * @dev Update compliance proof hash for the issuance 14 | * @param _newMerkleRoot New merkle root hash of the compliance Proofs 15 | * @param _complianceProof Compliance Proof hash 16 | * @return bool success 17 | */ 18 | function updateComplianceProof( 19 | bytes32 _newMerkleRoot, 20 | bytes32 _complianceProof 21 | ) public returns (bool success); 22 | 23 | /** 24 | * @dev `selectOfferingFactory` Select an security token offering proposal for the issuance 25 | * @param _offeringFactoryProposalIndex Array index of the STO proposal 26 | * @return bool success 27 | */ 28 | function selectOfferingFactory ( 29 | uint8 _offeringFactoryProposalIndex 30 | ) public returns (bool success); 31 | 32 | /** 33 | * @dev Start the offering by sending all the tokens to STO contract 34 | * @param _startTime Unix timestamp to start the offering 35 | * @param _endTime Unix timestamp to end the offering 36 | * @param _polyTokenRate Price of one security token in terms of poly 37 | * @param _maxPoly Maximum amount of poly issuer wants to collect 38 | * @param _lockupPeriod Length of time raised POLY will be locked up for dispute 39 | * @param _quorum Percent of initial investors required to freeze POLY raise 40 | * @return bool 41 | */ 42 | function initialiseOffering(uint256 _startTime, uint256 _endTime, uint256 _polyTokenRate, uint256 _maxPoly, uint256 _lockupPeriod, uint8 _quorum) external returns (bool success); 43 | 44 | /** 45 | * @dev Add a verified address to the Security Token whitelist 46 | * @param _whitelistAddress Address attempting to join ST whitelist 47 | * @return bool success 48 | */ 49 | function addToWhitelist(address _whitelistAddress) public returns (bool success); 50 | 51 | /** 52 | * @dev Add verified addresses to the Security Token whitelist 53 | * @param _whitelistAddresses Array of addresses attempting to join ST whitelist 54 | * @return bool success 55 | */ 56 | function addToWhitelistMulti(address[] _whitelistAddresses) public returns (bool success); 57 | 58 | /** 59 | * @dev Removes a previosly verified address to the Security Token blacklist 60 | * @param _blacklistAddress Address being added to the blacklist 61 | * @return bool success 62 | */ 63 | function addToBlacklist(address _blacklistAddress) public returns (bool success); 64 | 65 | /** 66 | * @dev Removes previously verified addresseses to the Security Token whitelist 67 | * @param _blacklistAddresses Array of addresses attempting to join ST whitelist 68 | * @return bool success 69 | */ 70 | function addToBlacklistMulti(address[] _blacklistAddresses) public returns (bool success); 71 | 72 | /** 73 | * @dev Allow POLY allocations to be withdrawn by owner, delegate, and the STO auditor at appropriate times 74 | * @return bool success 75 | */ 76 | function withdrawPoly() public returns (bool success); 77 | 78 | /** 79 | * @dev Vote to freeze the fee of a certain network participant 80 | * @param _recipient The fee recipient being protested 81 | * @return bool success 82 | */ 83 | function voteToFreeze(address _recipient) public returns (bool success); 84 | 85 | /** 86 | * @dev `issueSecurityTokens` is used by the STO to keep track of STO investors 87 | * @param _contributor The address of the person whose contributing 88 | * @param _amountOfSecurityTokens The amount of ST to pay out. 89 | * @param _polyContributed The amount of POLY paid for the security tokens. 90 | */ 91 | function issueSecurityTokens(address _contributor, uint256 _amountOfSecurityTokens, uint256 _polyContributed) public returns (bool success); 92 | 93 | /// Get token details 94 | function getTokenDetails() view public returns (address, address, bytes32, address, address, address); 95 | 96 | /// Get token decimals 97 | function decimals() view public returns (uint8); 98 | } 99 | 100 | /// ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) 101 | interface IERC20 { 102 | function balanceOf(address _owner) public view returns (uint256 balance); 103 | function transfer(address _to, uint256 _value) public returns (bool success); 104 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 105 | function approve(address _spender, uint256 _value) public returns (bool success); 106 | function allowance(address _owner, address _spender) public view returns (uint256 remaining); 107 | function totalSupply() public view returns (uint256); 108 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 109 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 110 | } 111 | 112 | /** 113 | * SafeMath 114 | * Copyright (c) 2016 Smart Contract Solutions, Inc. 115 | * Released under the MIT License (MIT) 116 | */ 117 | 118 | /// @title Math operations with safety checks 119 | library SafeMath { 120 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 121 | uint256 c = a * b; 122 | assert(a == 0 || c / a == b); 123 | return c; 124 | } 125 | 126 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 127 | // assert(b > 0); // Solidity automatically throws when dividing by 0 128 | uint256 c = a / b; 129 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 130 | return c; 131 | } 132 | 133 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 134 | assert(b <= a); 135 | return a - b; 136 | } 137 | 138 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 139 | uint256 c = a + b; 140 | assert(c >= a); 141 | return c; 142 | } 143 | 144 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 145 | return a >= b ? a : b; 146 | } 147 | 148 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 149 | return a < b ? a : b; 150 | } 151 | 152 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 153 | return a >= b ? a : b; 154 | } 155 | 156 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 157 | return a < b ? a : b; 158 | } 159 | } 160 | 161 | contract SimpleCappedOffering { 162 | 163 | using SafeMath for uint256; 164 | string public VERSION = "1"; 165 | 166 | ISecurityToken public SecurityToken; 167 | 168 | uint256 public maxPoly; // Maximum Poly limit raised by the offering contract 169 | uint256 public polyRaised; // Variable to track the poly raised 170 | uint256 public startTime; // Unix timestamp to start the offering 171 | uint256 public endTime; // Unix timestamp to end the offering 172 | uint256 public exchangeRatePolyToken; // Fix rate of 1 security token in terms of POLY 173 | 174 | uint256 public securityTokensSold; // Amount of security tokens sold through the STO 175 | 176 | ///////////// 177 | // Constants 178 | ///////////// 179 | 180 | uint256 public constant DECIMALSFACTOR = 10**uint256(18); 181 | 182 | uint256 public constant TOKENS_MAX_TOTAL = 1000000 * DECIMALSFACTOR; // 100% 183 | uint256 public constant TOKENS_STO = 500000 * DECIMALSFACTOR; // 50% 184 | uint256 public constant TOKENS_FOUNDERS = 200000 * DECIMALSFACTOR; // 20% 185 | uint256 public constant TOKENS_EARLY_INVESTORS = 200000 * DECIMALSFACTOR; // 20% 186 | uint256 public constant TOKENS_ADVISORS = 100000 * DECIMALSFACTOR; // 10% 187 | 188 | /////////////// 189 | // MODIFIERS // 190 | /////////////// 191 | 192 | modifier onlyDuringSale() { 193 | require(hasStarted() && !hasEnded()); 194 | _; 195 | } 196 | 197 | modifier onlyAfterSale() { 198 | // require finalized is stronger than hasSaleEnded 199 | require(hasEnded()); 200 | _; 201 | } 202 | 203 | //////////// 204 | // EVENTS // 205 | //////////// 206 | 207 | event LogBoughtSecurityToken(address indexed _contributor, uint256 _ployContribution, uint256 _timestamp); 208 | 209 | /** 210 | * @dev Constructor A new instance of the capped offering contract get launch 211 | * everytime when the constructor called by the factory contract 212 | * @param _startTime Unix timestamp to start the offering 213 | * @param _endTime Unix timestamp to end the offering 214 | * @param _exchangeRatePolyToken Price of one security token in terms of poly 215 | * @param _maxPoly Maximum amount of poly issuer wants to collect 216 | * @param _securityToken Address of the security token 217 | */ 218 | 219 | function SimpleCappedOffering(uint256 _startTime, uint256 _endTime, uint256 _exchangeRatePolyToken, uint256 _maxPoly, address _securityToken) public { 220 | require(_startTime >= now); 221 | require(_endTime > _startTime); 222 | require(_exchangeRatePolyToken > 0); 223 | require(_securityToken != address(0)); 224 | 225 | //TOKENS_MAX_TOTAL MUST BE equal to all other token token allocations combined 226 | require(TOKENS_STO.add(TOKENS_FOUNDERS).add(TOKENS_EARLY_INVESTORS).add(TOKENS_ADVISORS) == TOKENS_MAX_TOTAL); 227 | 228 | startTime = _startTime; 229 | endTime = _endTime; 230 | exchangeRatePolyToken = _exchangeRatePolyToken; 231 | maxPoly = _maxPoly; 232 | SecurityToken = ISecurityToken(_securityToken); 233 | } 234 | 235 | /** 236 | * @dev `buy` Facilitate the buying of SecurityToken in exchange of POLY 237 | * @param _polyContributed Amount of POLY contributor want to invest. 238 | * @return bool 239 | */ 240 | function buy(uint256 _polyContributed) public onlyDuringSale returns(bool) { 241 | require(_polyContributed > 0); 242 | require(validPurchase(_polyContributed)); 243 | uint256 _amountOfSecurityTokens = _polyContributed.div(exchangeRatePolyToken); 244 | 245 | // Make sure we don't sell more tokens than those available to the STO 246 | // TBD change this so we can sell the difference. 247 | require (securityTokensSold.add(_amountOfSecurityTokens) <= TOKENS_STO); 248 | 249 | require(SecurityToken.issueSecurityTokens(msg.sender, _amountOfSecurityTokens, _polyContributed)); 250 | 251 | polyRaised = polyRaised.add(_polyContributed); 252 | securityTokensSold = securityTokensSold.add(_amountOfSecurityTokens); //Keep track of tokens sold 253 | 254 | LogBoughtSecurityToken(msg.sender, _polyContributed, now); 255 | 256 | return true; 257 | } 258 | 259 | /** 260 | * @dev Use to validate the poly contribution 261 | * If issuer sets the capping over the offering contract then raised amount should 262 | * always less than or equal to the maximum amount set (maxPoly) 263 | * @param _polyContributed Amount of POLY contributor want to invest 264 | * @return bool 265 | */ 266 | function validPurchase(uint256 _polyContributed) internal view returns(bool) { 267 | bool reachedCap = maxPoly > 0 && polyRaised.add(_polyContributed) <= maxPoly; 268 | return (reachedCap); 269 | } 270 | 271 | // 272 | //Helper functions for onlyDuringSale / onlyAfterSale modifiers 273 | // 274 | 275 | // @return true if STO has ended 276 | function hasEnded() public constant returns (bool) { 277 | return now > endTime; 278 | } 279 | 280 | // @return true if STO has started 281 | function hasStarted() public constant returns (bool) { 282 | return now >= startTime; 283 | } 284 | 285 | } -------------------------------------------------------------------------------- /flat/OfferingFactories/SimpleCappedOfferingFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ISecurityToken { 4 | 5 | /** 6 | * @dev `selectTemplate` Select a proposed template for the issuance 7 | * @param _templateIndex Array index of the delegates proposed template 8 | * @return bool success 9 | */ 10 | function selectTemplate(uint8 _templateIndex) public returns (bool success); 11 | 12 | /** 13 | * @dev Update compliance proof hash for the issuance 14 | * @param _newMerkleRoot New merkle root hash of the compliance Proofs 15 | * @param _complianceProof Compliance Proof hash 16 | * @return bool success 17 | */ 18 | function updateComplianceProof( 19 | bytes32 _newMerkleRoot, 20 | bytes32 _complianceProof 21 | ) public returns (bool success); 22 | 23 | /** 24 | * @dev `selectOfferingFactory` Select an security token offering proposal for the issuance 25 | * @param _offeringFactoryProposalIndex Array index of the STO proposal 26 | * @return bool success 27 | */ 28 | function selectOfferingFactory ( 29 | uint8 _offeringFactoryProposalIndex 30 | ) public returns (bool success); 31 | 32 | /** 33 | * @dev Start the offering by sending all the tokens to STO contract 34 | * @param _startTime Unix timestamp to start the offering 35 | * @param _endTime Unix timestamp to end the offering 36 | * @param _polyTokenRate Price of one security token in terms of poly 37 | * @param _maxPoly Maximum amount of poly issuer wants to collect 38 | * @param _lockupPeriod Length of time raised POLY will be locked up for dispute 39 | * @param _quorum Percent of initial investors required to freeze POLY raise 40 | * @return bool 41 | */ 42 | function initialiseOffering(uint256 _startTime, uint256 _endTime, uint256 _polyTokenRate, uint256 _maxPoly, uint256 _lockupPeriod, uint8 _quorum) external returns (bool success); 43 | 44 | /** 45 | * @dev Add a verified address to the Security Token whitelist 46 | * @param _whitelistAddress Address attempting to join ST whitelist 47 | * @return bool success 48 | */ 49 | function addToWhitelist(address _whitelistAddress) public returns (bool success); 50 | 51 | /** 52 | * @dev Add verified addresses to the Security Token whitelist 53 | * @param _whitelistAddresses Array of addresses attempting to join ST whitelist 54 | * @return bool success 55 | */ 56 | function addToWhitelistMulti(address[] _whitelistAddresses) public returns (bool success); 57 | 58 | /** 59 | * @dev Removes a previosly verified address to the Security Token blacklist 60 | * @param _blacklistAddress Address being added to the blacklist 61 | * @return bool success 62 | */ 63 | function addToBlacklist(address _blacklistAddress) public returns (bool success); 64 | 65 | /** 66 | * @dev Removes previously verified addresseses to the Security Token whitelist 67 | * @param _blacklistAddresses Array of addresses attempting to join ST whitelist 68 | * @return bool success 69 | */ 70 | function addToBlacklistMulti(address[] _blacklistAddresses) public returns (bool success); 71 | 72 | /** 73 | * @dev Allow POLY allocations to be withdrawn by owner, delegate, and the STO auditor at appropriate times 74 | * @return bool success 75 | */ 76 | function withdrawPoly() public returns (bool success); 77 | 78 | /** 79 | * @dev Vote to freeze the fee of a certain network participant 80 | * @param _recipient The fee recipient being protested 81 | * @return bool success 82 | */ 83 | function voteToFreeze(address _recipient) public returns (bool success); 84 | 85 | /** 86 | * @dev `issueSecurityTokens` is used by the STO to keep track of STO investors 87 | * @param _contributor The address of the person whose contributing 88 | * @param _amountOfSecurityTokens The amount of ST to pay out. 89 | * @param _polyContributed The amount of POLY paid for the security tokens. 90 | */ 91 | function issueSecurityTokens(address _contributor, uint256 _amountOfSecurityTokens, uint256 _polyContributed) public returns (bool success); 92 | 93 | /// Get token details 94 | function getTokenDetails() view public returns (address, address, bytes32, address, address, address); 95 | 96 | /// Get token decimals 97 | function decimals() view public returns (uint8); 98 | } 99 | 100 | /// ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) 101 | interface IERC20 { 102 | function balanceOf(address _owner) public view returns (uint256 balance); 103 | function transfer(address _to, uint256 _value) public returns (bool success); 104 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 105 | function approve(address _spender, uint256 _value) public returns (bool success); 106 | function allowance(address _owner, address _spender) public view returns (uint256 remaining); 107 | function totalSupply() public view returns (uint256); 108 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 109 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 110 | } 111 | 112 | /** 113 | * SafeMath 114 | * Copyright (c) 2016 Smart Contract Solutions, Inc. 115 | * Released under the MIT License (MIT) 116 | */ 117 | 118 | /// @title Math operations with safety checks 119 | library SafeMath { 120 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 121 | uint256 c = a * b; 122 | assert(a == 0 || c / a == b); 123 | return c; 124 | } 125 | 126 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 127 | // assert(b > 0); // Solidity automatically throws when dividing by 0 128 | uint256 c = a / b; 129 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 130 | return c; 131 | } 132 | 133 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 134 | assert(b <= a); 135 | return a - b; 136 | } 137 | 138 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 139 | uint256 c = a + b; 140 | assert(c >= a); 141 | return c; 142 | } 143 | 144 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 145 | return a >= b ? a : b; 146 | } 147 | 148 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 149 | return a < b ? a : b; 150 | } 151 | 152 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 153 | return a >= b ? a : b; 154 | } 155 | 156 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 157 | return a < b ? a : b; 158 | } 159 | } 160 | 161 | contract SimpleCappedOffering { 162 | 163 | using SafeMath for uint256; 164 | string public VERSION = "1"; 165 | 166 | ISecurityToken public SecurityToken; 167 | 168 | uint256 public maxPoly; // Maximum Poly limit raised by the offering contract 169 | uint256 public polyRaised; // Variable to track the poly raised 170 | uint256 public startTime; // Unix timestamp to start the offering 171 | uint256 public endTime; // Unix timestamp to end the offering 172 | uint256 public exchangeRatePolyToken; // Fix rate of 1 security token in terms of POLY 173 | 174 | uint256 public securityTokensSold; // Amount of security tokens sold through the STO 175 | 176 | ///////////// 177 | // Constants 178 | ///////////// 179 | 180 | uint256 public constant DECIMALSFACTOR = 10**uint256(18); 181 | 182 | uint256 public constant TOKENS_MAX_TOTAL = 1000000 * DECIMALSFACTOR; // 100% 183 | uint256 public constant TOKENS_STO = 500000 * DECIMALSFACTOR; // 50% 184 | uint256 public constant TOKENS_FOUNDERS = 200000 * DECIMALSFACTOR; // 20% 185 | uint256 public constant TOKENS_EARLY_INVESTORS = 200000 * DECIMALSFACTOR; // 20% 186 | uint256 public constant TOKENS_ADVISORS = 100000 * DECIMALSFACTOR; // 10% 187 | 188 | /////////////// 189 | // MODIFIERS // 190 | /////////////// 191 | 192 | modifier onlyDuringSale() { 193 | require(hasStarted() && !hasEnded()); 194 | _; 195 | } 196 | 197 | modifier onlyAfterSale() { 198 | // require finalized is stronger than hasSaleEnded 199 | require(hasEnded()); 200 | _; 201 | } 202 | 203 | //////////// 204 | // EVENTS // 205 | //////////// 206 | 207 | event LogBoughtSecurityToken(address indexed _contributor, uint256 _ployContribution, uint256 _timestamp); 208 | 209 | /** 210 | * @dev Constructor A new instance of the capped offering contract get launch 211 | * everytime when the constructor called by the factory contract 212 | * @param _startTime Unix timestamp to start the offering 213 | * @param _endTime Unix timestamp to end the offering 214 | * @param _exchangeRatePolyToken Price of one security token in terms of poly 215 | * @param _maxPoly Maximum amount of poly issuer wants to collect 216 | * @param _securityToken Address of the security token 217 | */ 218 | 219 | function SimpleCappedOffering(uint256 _startTime, uint256 _endTime, uint256 _exchangeRatePolyToken, uint256 _maxPoly, address _securityToken) public { 220 | require(_startTime >= now); 221 | require(_endTime > _startTime); 222 | require(_exchangeRatePolyToken > 0); 223 | require(_securityToken != address(0)); 224 | 225 | //TOKENS_MAX_TOTAL MUST BE equal to all other token token allocations combined 226 | require(TOKENS_STO.add(TOKENS_FOUNDERS).add(TOKENS_EARLY_INVESTORS).add(TOKENS_ADVISORS) == TOKENS_MAX_TOTAL); 227 | 228 | startTime = _startTime; 229 | endTime = _endTime; 230 | exchangeRatePolyToken = _exchangeRatePolyToken; 231 | maxPoly = _maxPoly; 232 | SecurityToken = ISecurityToken(_securityToken); 233 | } 234 | 235 | /** 236 | * @dev `buy` Facilitate the buying of SecurityToken in exchange of POLY 237 | * @param _polyContributed Amount of POLY contributor want to invest. 238 | * @return bool 239 | */ 240 | function buy(uint256 _polyContributed) public onlyDuringSale returns(bool) { 241 | require(_polyContributed > 0); 242 | require(validPurchase(_polyContributed)); 243 | uint256 _amountOfSecurityTokens = _polyContributed.div(exchangeRatePolyToken); 244 | 245 | // Make sure we don't sell more tokens than those available to the STO 246 | // TBD change this so we can sell the difference. 247 | require (securityTokensSold.add(_amountOfSecurityTokens) <= TOKENS_STO); 248 | 249 | require(SecurityToken.issueSecurityTokens(msg.sender, _amountOfSecurityTokens, _polyContributed)); 250 | 251 | polyRaised = polyRaised.add(_polyContributed); 252 | securityTokensSold = securityTokensSold.add(_amountOfSecurityTokens); //Keep track of tokens sold 253 | 254 | LogBoughtSecurityToken(msg.sender, _polyContributed, now); 255 | 256 | return true; 257 | } 258 | 259 | /** 260 | * @dev Use to validate the poly contribution 261 | * If issuer sets the capping over the offering contract then raised amount should 262 | * always less than or equal to the maximum amount set (maxPoly) 263 | * @param _polyContributed Amount of POLY contributor want to invest 264 | * @return bool 265 | */ 266 | function validPurchase(uint256 _polyContributed) internal view returns(bool) { 267 | bool reachedCap = maxPoly > 0 && polyRaised.add(_polyContributed) <= maxPoly; 268 | return (reachedCap); 269 | } 270 | 271 | // 272 | //Helper functions for onlyDuringSale / onlyAfterSale modifiers 273 | // 274 | 275 | // @return true if STO has ended 276 | function hasEnded() public constant returns (bool) { 277 | return now > endTime; 278 | } 279 | 280 | // @return true if STO has started 281 | function hasStarted() public constant returns (bool) { 282 | return now >= startTime; 283 | } 284 | 285 | } 286 | 287 | interface IOfferingFactory { 288 | 289 | /** 290 | * @dev It facilitate the creation of the STO contract with essentials parameters 291 | * @param _startTime Unix timestamp to start the offering 292 | * @param _endTime Unix timestamp to end the offering 293 | * @param _polyTokenRate Price of one security token in terms of poly 294 | * @param _maxPoly Maximum amount of poly issuer wants to collect 295 | * @return address Address of the new offering instance 296 | */ 297 | function createOffering( 298 | uint256 _startTime, 299 | uint256 _endTime, 300 | uint256 _polyTokenRate, 301 | uint256 _maxPoly 302 | ) public returns (address); 303 | 304 | /** 305 | * @dev `getUsageDetails` is a function to get all the details on factory usage fees 306 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, string description 307 | */ 308 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, bytes32); 309 | 310 | } 311 | 312 | /** 313 | * @dev Highly Recommended - Only a sample STO factory Not used for mainnet !! 314 | */ 315 | 316 | contract SimpleCappedOfferingFactory is IOfferingFactory { 317 | 318 | using SafeMath for uint256; 319 | string public VERSION = "1"; 320 | 321 | ISecurityToken public SecurityToken; 322 | 323 | uint256 public fee = 100; 324 | uint8 public quorum = 10; 325 | uint256 public vestingPeriod = 8888888; 326 | bytes32 public description = "Capped"; 327 | uint256 public fxPolyToken; 328 | 329 | address public owner; 330 | 331 | function SimpleCappedOfferingFactory() public { 332 | owner = msg.sender; 333 | } 334 | 335 | /** 336 | * @dev It facilitate the creation of the STO contract with essentials parameters 337 | * @param _startTime Unix timestamp to start the offering 338 | * @param _endTime Unix timestamp to end the offering 339 | * @param _polyTokenRate Price of one security token in terms of poly 340 | * @param _maxPoly Maximum amount of poly issuer wants to collect 341 | * @return address Address of the new offering instance 342 | */ 343 | function createOffering( 344 | uint256 _startTime, 345 | uint256 _endTime, 346 | uint256 _polyTokenRate, 347 | uint256 _maxPoly 348 | ) public returns (address) 349 | { 350 | return new SimpleCappedOffering(_startTime, _endTime, _polyTokenRate, _maxPoly, msg.sender); 351 | } 352 | 353 | /** 354 | * @dev `getUsageDetails` is a function to get all the details on factory usage fees 355 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, string description 356 | */ 357 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, bytes32) { 358 | return (fee, quorum, vestingPeriod, owner, description); 359 | } 360 | 361 | } -------------------------------------------------------------------------------- /flat/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * SafeMath 5 | * Copyright (c) 2016 Smart Contract Solutions, Inc. 6 | * Released under the MIT License (MIT) 7 | */ 8 | 9 | /// @title Math operations with safety checks 10 | library SafeMath { 11 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 12 | uint256 c = a * b; 13 | assert(a == 0 || c / a == b); 14 | return c; 15 | } 16 | 17 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 18 | // assert(b > 0); // Solidity automatically throws when dividing by 0 19 | uint256 c = a / b; 20 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 21 | return c; 22 | } 23 | 24 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 25 | assert(b <= a); 26 | return a - b; 27 | } 28 | 29 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 30 | uint256 c = a + b; 31 | assert(c >= a); 32 | return c; 33 | } 34 | 35 | function max64(uint64 a, uint64 b) internal pure returns (uint64) { 36 | return a >= b ? a : b; 37 | } 38 | 39 | function min64(uint64 a, uint64 b) internal pure returns (uint64) { 40 | return a < b ? a : b; 41 | } 42 | 43 | function max256(uint256 a, uint256 b) internal pure returns (uint256) { 44 | return a >= b ? a : b; 45 | } 46 | 47 | function min256(uint256 a, uint256 b) internal pure returns (uint256) { 48 | return a < b ? a : b; 49 | } 50 | } -------------------------------------------------------------------------------- /flat/Template.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | interface ITemplate { 4 | 5 | /** 6 | * @dev `addJurisdiction` allows the adding of new jurisdictions to a template 7 | * @param _allowedJurisdictions An array of jurisdictions 8 | * @param _allowed An array of whether the jurisdiction is allowed to purchase the security or not 9 | */ 10 | function addJurisdiction(bytes32[] _allowedJurisdictions, bool[] _allowed) public; 11 | 12 | /** 13 | * @dev `addDivisionJurisdiction` allows the adding of new jurisdictions to a template 14 | * @param _blockedDivisionJurisdictions An array of jurisdictions 15 | * @param _blocked An array of whether the jurisdiction is allowed to purchase the security or not 16 | */ 17 | function addDivisionJurisdiction(bytes32[] _blockedDivisionJurisdictions, bool[] _blocked) public; 18 | 19 | /** 20 | * @dev `addRole` allows the adding of new roles to be added to whitelist 21 | * @param _allowedRoles User roles that can purchase the security 22 | */ 23 | function addRoles(uint8[] _allowedRoles) public; 24 | 25 | /** 26 | * @notice `updateDetails` 27 | * @param _details details of the template need to change 28 | * @return allowed boolean variable 29 | */ 30 | function updateDetails(bytes32 _details) public returns (bool allowed); 31 | 32 | /** 33 | * @dev `finalizeTemplate` is used to finalize template.full compliance process/requirements 34 | * @return success 35 | */ 36 | function finalizeTemplate() public returns (bool success); 37 | 38 | /** 39 | * @dev `checkTemplateRequirements` is a constant function that checks if templates requirements are met 40 | * @param _countryJurisdiction The ISO-3166 code of the investors country jurisdiction 41 | * @param _divisionJurisdiction The ISO-3166 code of the investors subdivision jurisdiction 42 | * @param _accredited Whether the investor is accredited or not 43 | * @param _role role of the user 44 | * @return allowed boolean variable 45 | */ 46 | function checkTemplateRequirements( 47 | bytes32 _countryJurisdiction, 48 | bytes32 _divisionJurisdiction, 49 | bool _accredited, 50 | uint8 _role 51 | ) public view returns (bool allowed); 52 | 53 | /** 54 | * @dev getTemplateDetails is a constant function that gets template details 55 | * @return bytes32 details, bool finalized 56 | */ 57 | function getTemplateDetails() view public returns (bytes32, bool); 58 | 59 | /** 60 | * @dev `getUsageDetails` is a function to get all the details on template usage fees 61 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC 62 | */ 63 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, address); 64 | 65 | /** 66 | * @dev Get the list of allowed jurisdictions 67 | * @return bytes32[] 68 | */ 69 | function getAllowedJurisdictionsList() view public returns (bytes32[]); 70 | 71 | /** 72 | * @dev Get the list of allowed roles 73 | * @return uin8[] 74 | */ 75 | function getAllowedRolesList() view public returns (uint8[]); 76 | 77 | /** 78 | * @dev Get the list of allowed roles 79 | * @return bytes32[] 80 | */ 81 | function getblockedDivisionJurisdictionsList() view public returns (bytes32[]); 82 | } 83 | 84 | /* 85 | Polymath compliance template is intended to ensure regulatory compliance 86 | in the jurisdictions that security tokens are being offered in. The compliance 87 | template allows security tokens to enforce purchase restrictions on chain and 88 | keep a log of documents for future auditing purposes. 89 | */ 90 | 91 | 92 | 93 | /** 94 | * @title Template 95 | * @dev Template details used for the security token offering to ensure the regulatory compliance 96 | */ 97 | 98 | contract Template is ITemplate { 99 | 100 | string public VERSION = "1"; 101 | 102 | address public owner; // Address of the owner of template 103 | string public offeringType; // Name of the security being issued 104 | bytes32 public issuerJurisdiction; // Variable contains the jurisdiction of the issuer of the template 105 | mapping(bytes32 => bool) public allowedJurisdictions; // Mapping that contains the allowed staus of Jurisdictions 106 | mapping(bytes32 => bool) public blockedDivisionJurisdictions; // Mapping that contains the allowed staus of Jurisdictions 107 | mapping(uint8 => bool) public allowedRoles; // Mapping that contains the allowed status of Roles 108 | bytes32[] public allowedJurisdictionsList; // List of allowed jurisdiction in the template 109 | bytes32[] public blockedDivisionJurisdictionsList; // List of blocked divison jurisdiction in the template 110 | uint8[] public allowedRolesList; // List of allowed roles list 111 | bool public accredited; // Variable that define the required level of accrediation for the investor 112 | address public KYC; // Address of the KYC provider 113 | bytes32 details; // Details of the offering requirements 114 | bool finalized; // Variable to know the status of the template (complete - true, not complete - false) 115 | uint256 public expires; // Timestamp when template expires 116 | uint256 fee; // Amount of POLY to use the template (held in escrow until issuance) 117 | uint8 quorum; // Minimum percent of shareholders which need to vote to freeze 118 | uint256 vestingPeriod; // Length of time to vest funds 119 | 120 | uint removedJurisdictionsCount; // Keeps track of how many jurisdictions have been removed from allowed list for this template 121 | // Notification 122 | event DetailsUpdated(bytes32 _prevDetails, bytes32 _newDetails, uint _updateDate); 123 | event LogFinalizedTemplate(bool _finalized, uint256 _timestamp); 124 | 125 | function Template ( 126 | address _owner, 127 | string _offeringType, 128 | bytes32 _issuerJurisdiction, 129 | bool _accredited, 130 | address _KYC, 131 | bytes32 _details, 132 | uint256 _expires, 133 | uint256 _fee, 134 | uint8 _quorum, 135 | uint256 _vestingPeriod 136 | ) public 137 | { 138 | require(_KYC != address(0) && _owner != address(0)); 139 | require(_details.length > 0 && _expires > now && _issuerJurisdiction.length > 0); 140 | require(_quorum > 0 && _quorum <= 100); 141 | require(_vestingPeriod > 0); 142 | owner = _owner; 143 | offeringType = _offeringType; 144 | issuerJurisdiction = _issuerJurisdiction; 145 | accredited = _accredited; 146 | KYC = _KYC; 147 | details = _details; 148 | finalized = false; 149 | expires = _expires; 150 | fee = _fee; 151 | quorum = _quorum; 152 | vestingPeriod = _vestingPeriod; 153 | } 154 | 155 | /** 156 | * @dev `addJurisdiction` allows the adding of new jurisdictions to a template 157 | * @param _allowedJurisdictions An array of jurisdictions 158 | * @param _allowed An array of whether the jurisdiction is allowed to purchase the security or not 159 | */ 160 | function addJurisdiction(bytes32[] _allowedJurisdictions, bool[] _allowed) public { 161 | require(owner == msg.sender); 162 | require(_allowedJurisdictions.length == _allowed.length); 163 | require(!finalized); 164 | for (uint i = 0; i < _allowedJurisdictions.length; ++i) { 165 | if (!allowedJurisdictions[_allowedJurisdictions[i]] && _allowed[i]) 166 | allowedJurisdictionsList.push(_allowedJurisdictions[i]); 167 | else if (allowedJurisdictions[_allowedJurisdictions[i]] && !_allowed[i]) { 168 | removeFromJurisdictionList(_allowedJurisdictions[i]); 169 | removedJurisdictionsCount++; 170 | } 171 | 172 | allowedJurisdictions[_allowedJurisdictions[i]] = _allowed[i]; 173 | } 174 | } 175 | 176 | /** 177 | * @dev `addDivisionJurisdiction` allows the adding of new jurisdictions to a template 178 | * @param _blockedDivisionJurisdictions An array of subdivision jurisdictions 179 | * @param _blocked An array of whether the subdivision jurisdiction is blocked to purchase the security or not 180 | */ 181 | function addDivisionJurisdiction(bytes32[] _blockedDivisionJurisdictions, bool[] _blocked) public { 182 | require(owner == msg.sender); 183 | require(_blockedDivisionJurisdictions.length == _blocked.length); 184 | require(!finalized); 185 | for (uint i = 0; i < _blockedDivisionJurisdictions.length; ++i) { 186 | if (!blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] && _blocked[i]) 187 | blockedDivisionJurisdictionsList.push(_blockedDivisionJurisdictions[i]); 188 | else if (blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] && !_blocked[i]) { 189 | removeFromDivisionJurisdictionList(_blockedDivisionJurisdictions[i]); 190 | } 191 | 192 | blockedDivisionJurisdictions[_blockedDivisionJurisdictions[i]] = _blocked[i]; 193 | } 194 | } 195 | 196 | /** 197 | * @dev remove the jurisdiction from the allowed list of jurisdictions 198 | * @param _jurisdiction Jurisdiction which need to be removed 199 | */ 200 | function removeFromJurisdictionList(bytes32 _jurisdiction) internal { 201 | for (uint i = 0; i < allowedJurisdictionsList.length; i++) { 202 | if (allowedJurisdictionsList[i] == _jurisdiction) 203 | allowedJurisdictionsList[i] = 0x0; 204 | } 205 | } 206 | 207 | 208 | /** 209 | * @dev remove the divisionJurisdiction from the blocked list of divisionJurisdiction 210 | * @param _blockedDivisionJurisdiction divisionJurisdiction which need to be removed 211 | */ 212 | function removeFromDivisionJurisdictionList(bytes32 _blockedDivisionJurisdiction) internal { 213 | for (uint i = 0; i < blockedDivisionJurisdictionsList.length; i++) { 214 | if (blockedDivisionJurisdictionsList[i] == _blockedDivisionJurisdiction) 215 | blockedDivisionJurisdictionsList[i] = 0x0; 216 | } 217 | } 218 | 219 | /** 220 | * @dev `addRoles` allows the adding of new roles to be added to whitelist 221 | * @param _allowedRoles User roles that can purchase the security 222 | */ 223 | function addRoles(uint8[] _allowedRoles) public { 224 | require(owner == msg.sender); 225 | require(!finalized); 226 | for (uint i = 0; i < _allowedRoles.length; ++i) { 227 | if(!allowedRoles[_allowedRoles[i]]) 228 | allowedRolesList.push(_allowedRoles[i]); 229 | 230 | allowedRoles[_allowedRoles[i]] = true; 231 | } 232 | } 233 | 234 | /** 235 | * @notice `updateDetails` 236 | * @param _details details of the template need to change 237 | * @return allowed boolean variable 238 | */ 239 | function updateDetails(bytes32 _details) public returns (bool allowed) { 240 | require(_details != 0x0); 241 | require(owner == msg.sender); 242 | bytes32 prevDetails = details; 243 | details = _details; 244 | DetailsUpdated(prevDetails, details, now); 245 | return true; 246 | } 247 | 248 | /** 249 | * @dev `finalizeTemplate` is used to finalize template.full compliance process/requirements 250 | * @return success 251 | */ 252 | function finalizeTemplate() public returns (bool success) { 253 | require(owner == msg.sender); 254 | require(removedJurisdictionsCount != allowedJurisdictionsList.length); 255 | require(allowedRolesList.length > 0); 256 | finalized = true; 257 | LogFinalizedTemplate(finalized, now); 258 | return true; 259 | } 260 | 261 | /** 262 | * @dev `checkTemplateRequirements` is a constant function that checks if templates requirements are met 263 | * @param _countryJurisdiction The ISO-3166 code of the investors country jurisdiction 264 | * @param _divisionJurisdiction The ISO-3166 code of the investors subdivision jurisdiction 265 | * @param _accredited Whether the investor is accredited or not 266 | * @param _role role of the user 267 | * @return allowed boolean variable 268 | */ 269 | function checkTemplateRequirements( 270 | bytes32 _countryJurisdiction, 271 | bytes32 _divisionJurisdiction, 272 | bool _accredited, 273 | uint8 _role 274 | ) public view returns (bool allowed) 275 | { 276 | require(_countryJurisdiction != 0x0); 277 | require(allowedJurisdictions[_countryJurisdiction] || !blockedDivisionJurisdictions[_divisionJurisdiction]); 278 | require(allowedRoles[_role]); 279 | if (accredited) { 280 | require(_accredited); 281 | } 282 | return true; 283 | } 284 | 285 | /** 286 | * @dev getTemplateDetails is a constant function that gets template details 287 | * @return bytes32 details, bool finalized 288 | */ 289 | function getTemplateDetails() view public returns (bytes32, bool) { 290 | require(expires > now); 291 | return (details, finalized); 292 | } 293 | 294 | /** 295 | * @dev `getUsageDetails` is a function to get all the details on template usage fees 296 | * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC 297 | */ 298 | function getUsageDetails() view public returns (uint256, uint8, uint256, address, address) { 299 | return (fee, quorum, vestingPeriod, owner, KYC); 300 | } 301 | 302 | /** 303 | * @dev Get the list of allowed jurisdictions 304 | * @return bytes32[] 305 | */ 306 | function getAllowedJurisdictionsList() view public returns (bytes32[]) { 307 | return allowedJurisdictionsList; 308 | } 309 | 310 | /** 311 | * @dev Get the list of allowed roles 312 | * @return uin8[] 313 | */ 314 | function getAllowedRolesList() view public returns (uint8[]) { 315 | return allowedRolesList; 316 | } 317 | 318 | /** 319 | * @dev Get the list of allowed roles 320 | * @return bytes32[] 321 | */ 322 | function getblockedDivisionJurisdictionsList() view public returns (bytes32[]) { 323 | return blockedDivisionJurisdictionsList; 324 | } 325 | } -------------------------------------------------------------------------------- /migrations/1_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const Compliance = artifacts.require('./Compliance.sol'); 2 | const Customers = artifacts.require('./Customers.sol'); 3 | const SecurityToken = artifacts.require('./SecurityToken.sol'); 4 | const SecurityTokenRegistrar = artifacts.require('./SecurityTokenRegistrar.sol'); 5 | const NameSpaceRegistrar = artifacts.require('./NameSpaceRegistrar.sol'); 6 | const SimpleCappedOfferingFactory = artifacts.require('./SimpleCappedOfferingFactory.sol'); 7 | //NB - these are Ropsten addresses 8 | const PolyToken = '0x96a62428509002a7ae5f6ad29e4750d852a3f3d7'; 9 | const PolyFeeAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; 10 | const Fee = 100000*10**18; 11 | 12 | module.exports = async (deployer, network) => { 13 | console.log(`Deploying Polymath Network Smart contracts to ${network}...`); 14 | try{ 15 | await deployer.deploy(Customers, PolyToken); 16 | await deployer.deploy(Compliance, Customers.address); 17 | await deployer.deploy(SecurityTokenRegistrar, PolyToken, Customers.address, Compliance.address); 18 | await deployer.deploy(NameSpaceRegistrar); 19 | let compliance = await Compliance.deployed() 20 | await compliance.setRegistrarAddress(SecurityTokenRegistrar.address); 21 | console.log(`\nPolymath Network Smart Contracts Deployed:\n 22 | PolyToken: ${PolyToken}\n 23 | Compliance: ${Compliance.address}\n 24 | Customers: ${Customers.address}\n 25 | SecurityTokenRegistrar: ${SecurityTokenRegistrar.address}\n 26 | NameSpaceRegistrar: ${NameSpaceRegistrar.address}\n 27 | `); 28 | } catch(err) { 29 | console.log('Deploy error', err); 30 | } 31 | }; 32 | 33 | // 34 | // Uncomment the lines below to deploy an offering factory with a developer account 35 | // 36 | 37 | // module.exports = async (deployer, network) => { 38 | // console.log(`Deploying Offering Factory to ${network}...`); 39 | // try{ 40 | // await deployer.deploy(SimpleCappedOfferingFactory); 41 | // console.log(`\nPolymath Network Smart Contracts Deployed:\n 42 | // Offering Factory: ${SimpleCappedOfferingFactory.address}\n 43 | // `); 44 | // } catch(err) { 45 | // console.log('Deploy error', err); 46 | // } 47 | // }; 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polymath-core", 3 | "author": "Polymath Inc.", 4 | "license": "MIT", 5 | "version": "0.2.1", 6 | "description": "Polymath Network Core Smart Contracts", 7 | "repository": "git@github.com:PolymathNetwork/polymath-core.git", 8 | "homepage": "https://github.com/polymathnetwork/polymath-core", 9 | "bugs": { 10 | "url": "https://github.com/polymathnetwork/polymath-core/issues" 11 | }, 12 | "main": "truffle.js", 13 | "scripts": { 14 | "test": "scripts/test.sh", 15 | "compile": "truffle compile --optimize-runs 200", 16 | "migrate-local": "truffle migrate --network=development --reset", 17 | "migrate-ropsten": "truffle migrate --network=ropsten --reset", 18 | "flatten": "sol-merger './contracts/*.sol' ./flat", 19 | "flatten-STOs": "sol-merger './contracts/OfferingFactories/*.sol' ./flat/OfferingFactories", 20 | "coverage": "./node_modules/.bin/solidity-coverage" 21 | }, 22 | "dependencies": { 23 | "babel-polyfill": "6.26.0", 24 | "babel-preset-es2015": "6.24.1", 25 | "babel-preset-stage-2": "6.24.1", 26 | "babel-preset-stage-3": "6.24.1", 27 | "babel-register": "6.26.0", 28 | "bignumber.js": "4.0.2", 29 | "codecov": "2.3.0", 30 | "ethereumjs-abi": "^0.6.5", 31 | "ethereumjs-testrpc": "4.1.1", 32 | "ethereumjs-util": "^5.1.3", 33 | "ethereumjs-wallet": "^0.6.0", 34 | "ethers": "^2.2.4", 35 | "lodash": "4.17.4", 36 | "mocha-lcov-reporter": "1.3.0", 37 | "solc": "0.4.17", 38 | "truffle": "4.0.1", 39 | "truffle-hdwallet-provider-privkey": "^0.1.0", 40 | "truffle-wallet-provider": "0.0.5", 41 | "xtend": "4.0.1", 42 | "yargs": "8.0.2" 43 | }, 44 | "devDependencies": { 45 | "should": "13.1.3", 46 | "sol-merger": "0.1.2", 47 | "solidity-coverage": "0.4.7", 48 | "testrpc": "0.0.1", 49 | "truffle-hdwallet-provider": "0.0.3" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sample-keystore: -------------------------------------------------------------------------------- 1 | {"id":"5dbbff8d-aed3-0064-5ece-db7ed2963d3f","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"ce0315975b6462a14c0a2abd96e9406b"},"ciphertext":"9c31a0508c13cb6c2e9c11e325433989986dbb164514ce7b640b82d59006abfc","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"fc0c182fca3660f3994589ce0c769384ea00f4c80a08b17b9d9d2850e2082733"},"mac":"41d7d7ef3888c86a38258be756379ae3a17a0930ca956f07d2aad8debe59ce5e"},"address":"0065a7a979776caf77a52fb50d8f8152a6476c5c","name":"sample","meta":"{\"description\":\"\",\"passwordHint\":\"\",\"timestamp\":1518546305530}"} -------------------------------------------------------------------------------- /sample-pass: -------------------------------------------------------------------------------- 1 | sample -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SOLIDITY_COVERAGE=true scripts/test.sh 4 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit script as soon as a command fails. 4 | set -o errexit 5 | 6 | # Executes cleanup function at script exit. 7 | trap cleanup EXIT 8 | 9 | cleanup() { 10 | # Kill the testrpc instance that we started (if we started one and if it's still running). 11 | if [ -n "$testrpc_pid" ] && ps -p $testrpc_pid > /dev/null; then 12 | kill -9 $testrpc_pid 13 | fi 14 | } 15 | 16 | if [ "$SOLIDITY_COVERAGE" = true ]; then 17 | testrpc_port=8545 18 | else 19 | testrpc_port=8545 20 | fi 21 | 22 | testrpc_running() { 23 | nc -z localhost "$testrpc_port" 24 | } 25 | 26 | start_testrpc() { 27 | # We define 10 accounts with balance 1M ether, needed for high-value tests. 28 | local accounts=( 29 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000" 30 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000" 31 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000" 32 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000" 33 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000" 34 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000" 35 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000" 36 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000" 37 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000" 38 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000" 39 | ) 40 | 41 | if [ "$SOLIDITY_COVERAGE" = true ]; then 42 | node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff --port "$testrpc_port" "${accounts[@]}" > /dev/null & 43 | else 44 | node_modules/.bin/testrpc --gasLimit 0xfffffffffff "${accounts[@]}" > /dev/null & 45 | fi 46 | 47 | testrpc_pid=$! 48 | } 49 | 50 | if testrpc_running; then 51 | echo "Using existing testrpc instance" 52 | else 53 | echo "Starting our own testrpc instance" 54 | start_testrpc 55 | fi 56 | 57 | if [ "$SOLIDITY_COVERAGE" = true ]; then 58 | node_modules/.bin/solidity-coverage 59 | 60 | if [ "$CONTINUOUS_INTEGRATION" = true ]; then 61 | cat coverage/lcov.info | node_modules/.bin/coveralls 62 | fi 63 | else 64 | node_modules/.bin/truffle test "$@" 65 | fi 66 | -------------------------------------------------------------------------------- /scripts/tokenRegistrations.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolymathNetwork/polymath-core-deprecated/40aec71d728413ae2f9c022c252254ffadcc86e4/scripts/tokenRegistrations.sh -------------------------------------------------------------------------------- /test/Customers.js: -------------------------------------------------------------------------------- 1 | import {ensureException, duration} from './helpers/utils.js'; 2 | import latestTime from './helpers/latestTime'; 3 | import {web3StringToBytes32, signData} from './helpers/signData'; 4 | import { pk } from './helpers/testprivateKey'; 5 | 6 | const Compliance = artifacts.require('Customers.sol'); 7 | const POLY = artifacts.require('./helpers/mockContracts/PolyTokenMock.sol'); 8 | const Customers = artifacts.require('Customers.sol'); 9 | const BigNumber = require('bignumber.js'); 10 | 11 | const ethers = require('ethers'); 12 | const utils = ethers.utils; 13 | const ethUtil = require('ethereumjs-util'); 14 | 15 | contract('Customers', accounts => { 16 | 17 | //accounts 18 | let owner = accounts[0]; 19 | let customer1 = accounts[1]; 20 | let customer2 = accounts[2]; 21 | let provider1 = accounts[3]; 22 | let provider2 = accounts[4]; 23 | let attestor1 = accounts[5]; 24 | let attestor2 = accounts[6]; 25 | let pk_customer1 = pk.account_1; 26 | let pk_customer2 = pk.account_2; 27 | 28 | //newCustomer() constants 29 | const jurisdiction0 = '0'; 30 | const jurisdiction0_0 = '0_1'; 31 | const jurisdiction1 = '1'; 32 | const jurisdiction1_0 = '1_1'; 33 | const customerInvestorRole = 1; 34 | const customerIssuerRole = 2; 35 | 36 | //verifyCustomer() and approveProvider constants 37 | const expcurrentTime = latestTime(); //should get time currently 38 | const willExipres = latestTime() + duration.days(2); // After 2 days its customer verification status will expire 39 | 40 | //newProvider() constants 41 | const providerName1 = 'KYC-Chain'; 42 | const providerName2 = 'Uport'; 43 | const providerApplication1 = 'Details1'; 44 | const providerApplication2 = 'Details2'; 45 | const providerFee1 = 1000; 46 | const providerFee2 = 100; 47 | 48 | describe('function verifyCustomer', async () => { 49 | it('An approved and active KYC provider can validate customers as being in a jurisdiction and accredit a customer', async () => { 50 | let poly = await POLY.new(); 51 | let customers = await Customers.new(poly.address); 52 | 53 | await customers.newProvider( 54 | providerName1, 55 | providerApplication1, 56 | providerFee1, {from: provider1} 57 | ); 58 | 59 | // Providing allowance to the customer contract address to spend the POLY of Customer 60 | await poly.getTokens(10000, customer1, { from: customer1 }); 61 | await poly.approve(customers.address, 10000, { from: customer1 }); 62 | 63 | let nonce = 1; 64 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 65 | 66 | const r = `0x${sig.r.toString('hex')}`; 67 | const s = `0x${sig.s.toString('hex')}`; 68 | const v = sig.v; 69 | 70 | let isVerify = await customers.verifyCustomer( 71 | customer1, 72 | web3StringToBytes32(jurisdiction0), 73 | web3StringToBytes32(jurisdiction0_0), 74 | customerInvestorRole, 75 | true, 76 | willExipres, // 2 days more than current time 77 | nonce, 78 | v, 79 | r, 80 | s, 81 | { 82 | from: provider1, 83 | }, 84 | ); 85 | }); 86 | 87 | it('An approved and active KYC provider can validate customers twice with nonce increment', async () => { 88 | let poly = await POLY.new(); 89 | let customers = await Customers.new(poly.address); 90 | 91 | await customers.newProvider( 92 | providerName1, 93 | providerApplication1, 94 | providerFee1, {from: provider1} 95 | ); 96 | 97 | // Providing allowance to the customer contract address to spend the POLY of Customer 98 | await poly.getTokens(10000, customer1, { from: customer1 }); 99 | await poly.approve(customers.address, 10000, { from: customer1 }); 100 | 101 | let nonce = 1; 102 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 103 | 104 | const r = `0x${sig.r.toString('hex')}`; 105 | const s = `0x${sig.s.toString('hex')}`; 106 | const v = sig.v; 107 | 108 | let isVerify = await customers.verifyCustomer( 109 | customer1, 110 | web3StringToBytes32(jurisdiction0), 111 | web3StringToBytes32(jurisdiction0_0), 112 | customerInvestorRole, 113 | true, 114 | willExipres, // 2 days more than current time 115 | nonce, 116 | v, 117 | r, 118 | s, 119 | { 120 | from: provider1, 121 | }, 122 | ); 123 | 124 | let nonce2 = 2; 125 | const sig2 = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce2, pk_customer1); 126 | 127 | const r2 = `0x${sig2.r.toString('hex')}`; 128 | const s2 = `0x${sig2.s.toString('hex')}`; 129 | const v2 = sig2.v; 130 | 131 | let isVerify2 = await customers.verifyCustomer( 132 | customer1, 133 | web3StringToBytes32(jurisdiction0), 134 | web3StringToBytes32(jurisdiction0_0), 135 | customerInvestorRole, 136 | true, 137 | willExipres, // 2 days more than current time 138 | nonce2, 139 | v2, 140 | r2, 141 | s2, 142 | { 143 | from: provider1, 144 | }, 145 | ); 146 | 147 | }); 148 | 149 | it('An approved and active KYC provider cannot reuse nonce signature', async () => { 150 | let poly = await POLY.new(); 151 | let customers = await Customers.new(poly.address); 152 | 153 | await customers.newProvider( 154 | providerName1, 155 | providerApplication1, 156 | providerFee1, {from: provider1} 157 | ); 158 | 159 | // Providing allowance to the customer contract address to spend the POLY of Customer 160 | await poly.getTokens(10000, customer1, { from: customer1 }); 161 | await poly.approve(customers.address, 10000, { from: customer1 }); 162 | 163 | let nonce = 1; 164 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 165 | 166 | const r = `0x${sig.r.toString('hex')}`; 167 | const s = `0x${sig.s.toString('hex')}`; 168 | const v = sig.v; 169 | 170 | let isVerify = await customers.verifyCustomer( 171 | customer1, 172 | web3StringToBytes32(jurisdiction0), 173 | web3StringToBytes32(jurisdiction0_0), 174 | customerInvestorRole, 175 | true, 176 | willExipres, // 2 days more than current time 177 | nonce, 178 | v, 179 | r, 180 | s, 181 | { 182 | from: provider1, 183 | }, 184 | ); 185 | 186 | // let nonce2 = 1; 187 | // const sig2 = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, '2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201'); 188 | // 189 | // const r2 = `0x${sig2.r.toString('hex')}`; 190 | // const s2 = `0x${sig2.s.toString('hex')}`; 191 | // const v2 = sig.v; 192 | 193 | try { 194 | 195 | let isVerify = await customers.verifyCustomer( 196 | customer1, 197 | web3StringToBytes32(jurisdiction0), 198 | web3StringToBytes32(jurisdiction0_0), 199 | customerInvestorRole, 200 | true, 201 | willExipres, // 2 days more than current time 202 | nonce, 203 | v, 204 | r, 205 | s, 206 | { 207 | from: provider1, 208 | }, 209 | ); 210 | } catch(error) { 211 | ensureException(error); 212 | } 213 | 214 | 215 | }); 216 | 217 | it('verifyCustomer: An approved and active KYC provider can validate customers as being in a jurisdiction and accredit a customer -- fail due to expiry is less than now', async () => { 218 | let poly = await POLY.new(); 219 | let customers = await Customers.new(poly.address); 220 | 221 | await customers.newProvider( 222 | providerName1, 223 | providerApplication1, 224 | providerFee1, {from: provider1} 225 | ); 226 | // Providing allowance to the customer contract address to spend the POLY of Customer 227 | await poly.getTokens(10000, customer1, { from: customer1 }); 228 | await poly.approve(customers.address, 10000, { from: customer1 }); 229 | 230 | let nonce = 1; 231 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 232 | 233 | const r = `0x${sig.r.toString('hex')}`; 234 | const s = `0x${sig.s.toString('hex')}`; 235 | const v = sig.v; 236 | 237 | try { 238 | let isVerify = await customers.verifyCustomer.call( 239 | customer1, 240 | web3StringToBytes32(jurisdiction0), 241 | web3StringToBytes32(jurisdiction0_0), 242 | customerInvestorRole, 243 | true, 244 | (latestTime() - duration.hours(1)), // 1 hour before current time 245 | nonce, 246 | v, 247 | r, 248 | s, 249 | { 250 | from: provider1, 251 | }, 252 | ); 253 | } catch(error) { 254 | ensureException(error); 255 | } 256 | }); 257 | 258 | it('VerifyCustomer: Should fail due to the msg.sender is not provider', async () => { 259 | let poly = await POLY.new(); 260 | let customers = await Customers.new(poly.address); 261 | 262 | let providerOne = await customers.newProvider( 263 | providerName1, 264 | providerApplication1, 265 | providerFee1, {from: provider1} 266 | ); 267 | // Providing allowance to the customer contract address to spend the POLY of Customer 268 | await poly.getTokens(10000, customer1, { from: customer1 }); 269 | await poly.approve(customers.address, 10000, { from: customer1 }); 270 | 271 | let nonce = 1; 272 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 273 | 274 | const r = `0x${sig.r.toString('hex')}`; 275 | const s = `0x${sig.s.toString('hex')}`; 276 | const v = sig.v; 277 | 278 | try { 279 | let isVerify = await customers.verifyCustomer( 280 | customer1, 281 | web3StringToBytes32(jurisdiction0), 282 | web3StringToBytes32(jurisdiction0_0), 283 | customerInvestorRole, 284 | true, 285 | willExipres, // 2 days more than current time 286 | nonce, 287 | v, 288 | r, 289 | s, 290 | { 291 | from: customer2, 292 | }, 293 | ); 294 | } catch (error) { 295 | ensureException(error); 296 | } 297 | }); 298 | }); 299 | 300 | describe('function newProvider', async () => { 301 | it('newProvider: Should register the new KYC providers', async () => { 302 | let poly = await POLY.new(); 303 | let customers = await Customers.new(poly.address); 304 | 305 | await customers.newProvider( 306 | providerName1, 307 | providerApplication1, 308 | providerFee1, {from: provider1} 309 | ); 310 | let providerDetails = await customers.getProvider.call(provider1); 311 | assert.strictEqual(providerDetails[0].toString(), providerName1); // providerName1 = KYC-Chain 312 | }); 313 | 314 | it('newProvider: should kyc providers apply their data to chain -- fail because of zero details', async () => { 315 | let poly = await POLY.new(); 316 | let customers = await Customers.new(poly.address); 317 | 318 | try { 319 | await customers.newProvider( 320 | providerName1, 321 | 0x0, // Failed because details are zero 322 | providerFee1, {from: provider1} 323 | ); 324 | } catch (error) { 325 | ensureException(error); 326 | } 327 | }); 328 | 329 | }); 330 | 331 | describe("function changeFee", async () => { 332 | 333 | it('changeFee: Should allow to change the fee by the provider', async () => { 334 | let poly = await POLY.new(); 335 | let customers = await Customers.new(poly.address); 336 | 337 | await customers.newProvider( 338 | providerName1, 339 | providerApplication1, 340 | providerFee1, {from: provider1} 341 | ); 342 | 343 | await customers.changeFee(10000,{ from : provider1 }); 344 | let providerData = await customers.getProvider(provider1); 345 | assert.strictEqual(providerData[3].toNumber(),10000); 346 | }); 347 | 348 | it("changeFee: Should verify the customer with old fee then after change verify the new customer with new fee",async()=>{ 349 | let poly = await POLY.new(); 350 | let customers = await Customers.new(poly.address); 351 | 352 | await customers.newProvider( 353 | providerName1, 354 | providerApplication1, 355 | providerFee1, {from: provider1} 356 | ); 357 | 358 | // Providing allowance to the customer contract address to spend the POLY of Customer1 359 | await poly.getTokens(10000, customer1, { from: customer1 }); 360 | await poly.approve(customers.address, 10000, { from: customer1 }); 361 | 362 | let nonce = 1; 363 | const sig = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer1); 364 | 365 | const r = `0x${sig.r.toString('hex')}`; 366 | const s = `0x${sig.s.toString('hex')}`; 367 | const v = sig.v; 368 | 369 | let isVerifyTry1 = await customers.verifyCustomer( 370 | customer1, 371 | web3StringToBytes32(jurisdiction0), 372 | web3StringToBytes32(jurisdiction0_0), 373 | customerInvestorRole, 374 | true, 375 | willExipres, // 2 days more than current time 376 | nonce, 377 | v, 378 | r, 379 | s, 380 | { 381 | from: provider1, 382 | }, 383 | ); 384 | assert.strictEqual(isVerifyTry1.logs[0].args.customer, customer1); 385 | // Providing allowance to the customer contract address to spend the POLY of Customer2 386 | await poly.getTokens(10000, customer2, { from: customer2 }); 387 | await poly.approve(customers.address, 10000, { from: customer2 }); 388 | // Change fee that is charged by the provider 389 | await customers.changeFee(10000,{ from : provider1 }); 390 | let providerData = await customers.getProvider(provider1); 391 | assert.strictEqual(providerData[3].toNumber(),10000); 392 | 393 | const sig2 = signData(customers.address, provider1, jurisdiction0, jurisdiction0_0, customerInvestorRole, true, nonce, pk_customer2); 394 | 395 | const r2 = `0x${sig2.r.toString('hex')}`; 396 | const s2 = `0x${sig2.s.toString('hex')}`; 397 | const v2 = sig2.v; 398 | 399 | let isVerifyTry2 = await customers.verifyCustomer( 400 | customer2, 401 | web3StringToBytes32(jurisdiction0), 402 | web3StringToBytes32(jurisdiction0_0), 403 | customerInvestorRole, 404 | true, 405 | willExipres, // 2 days more than current time 406 | nonce, 407 | v2, 408 | r2, 409 | s2, 410 | { 411 | from: provider1, 412 | }, 413 | ); 414 | assert.strictEqual(isVerifyTry2.logs[0].args.customer, customer2); 415 | }); 416 | 417 | }); 418 | }); 419 | -------------------------------------------------------------------------------- /test/ERC20.js: -------------------------------------------------------------------------------- 1 | const POLY = artifacts.require('./helpers/mockContracts/PolyTokenMock.sol'); 2 | import {ensureException} from './helpers/utils.js' 3 | const BigNumber = require('bignumber.js'); 4 | 5 | 6 | 7 | contract("ERC20", (accounts) => { 8 | let owner ; 9 | let holder1; 10 | let holder2; 11 | let holder3; 12 | let holder4; 13 | let supply = 1000000; 14 | let trasferFunds = 150; 15 | let allowedAmount = 200; 16 | 17 | before(async() => { 18 | owner = accounts[0]; 19 | holder1 = accounts[1]; 20 | holder2 = accounts[2]; 21 | holder3 = accounts[3]; 22 | holder4 = accounts[4] 23 | }); 24 | describe("Constructor", async()=>{ 25 | it("Verify constructors",async()=>{ 26 | let poly = await POLY.new(); 27 | 28 | let tokenName = await poly.name.call(); 29 | assert.equal(tokenName.toString(),"Polymath Network"); 30 | 31 | let tokenSymbol = await poly.symbol(); 32 | assert.equal(tokenSymbol.toString(),"POLY"); 33 | 34 | let tokenSupply = await poly.totalSupply(); 35 | assert.equal(tokenSupply.toNumber(),supply); 36 | }); 37 | }); 38 | 39 | describe("transfer", async() => { 40 | it('transfer: ether directly to the token contract -- it will throw', async() => { 41 | let poly = await POLY.new(); 42 | try { 43 | await web3 44 | .eth 45 | .sendTransaction({ 46 | from: holder1, 47 | to: poly.address, 48 | value: web3.toWei('10', 'Ether') 49 | }); 50 | } catch (error) { 51 | ensureException(error); 52 | } 53 | }); 54 | 55 | it('transfer: should transfer 10000 to holder1 from owner', async() => { 56 | let poly = await POLY.new(); 57 | await poly.getTokens(100000,owner); 58 | await poly.transfer(holder1, 10000, {from: owner}); 59 | let balance = await poly 60 | .balanceOf 61 | .call(holder1); 62 | assert.strictEqual(balance.toNumber(), 10000); 63 | }); 64 | 65 | it('transfer: first should transfer 10000 to holder1 from owner then holder1 transfers 1000 to holder2', 66 | async() => { 67 | let poly = await POLY.new(); 68 | await poly.getTokens(100000,owner); 69 | await poly.transfer(holder1, 10000, {from: owner}); 70 | let balance = await poly 71 | .balanceOf 72 | .call(holder1); 73 | assert.strictEqual(balance.toNumber(), 10000); 74 | await poly.transfer(holder2, 1000, {from: holder1}); 75 | let accBalance = await poly 76 | .balanceOf 77 | .call(holder2); 78 | assert.strictEqual(accBalance.toNumber(), 1000); 79 | }); 80 | }); 81 | 82 | describe("approve", async() => { 83 | it('approve: holder1 should approve 1000 to holder2', async() => { 84 | let poly = await POLY.new(); 85 | await poly.getTokens(100000,owner); 86 | await poly.transfer(holder1, 10000, {from: owner}); 87 | await poly.approve(holder2, 1000, {from: holder1}); 88 | let _allowance = await poly 89 | .allowance 90 | .call(holder1, holder2); 91 | assert.strictEqual(_allowance.toNumber(),1000); 92 | }); 93 | 94 | it('approve: holder1 should approve 1000 to holder2 & withdraws 200 once', async() => { 95 | let poly = await POLY.new(); 96 | await poly.getTokens(100000,owner); 97 | await poly.transfer(holder1, 1000, {from: owner}); 98 | await poly.approve(holder2, 1000, {from: holder1}) 99 | let _allowance1 = await poly 100 | .allowance 101 | .call(holder1, holder2); 102 | assert.strictEqual(_allowance1.toNumber(), 1000); 103 | await poly.transferFrom(holder1, holder3, 200, {from: holder2}); 104 | let balance = await poly 105 | .balanceOf 106 | .call(holder3); 107 | assert.strictEqual(balance.toNumber(), 200); 108 | let _allowance2 = await poly 109 | .allowance 110 | .call(holder1, holder2); 111 | assert.strictEqual(_allowance2.toNumber(), 800); 112 | let _balance = await poly 113 | .balanceOf 114 | .call(holder1); 115 | assert.strictEqual(_balance.toNumber(), 800); 116 | }); 117 | 118 | it('approve: holder1 should approve 1000 to holder2 & withdraws 200 twice', async() => { 119 | let poly = await POLY.new(); 120 | await poly.getTokens(100000,owner); 121 | await poly.transfer(holder1, 1000, {from: owner}); 122 | await poly.approve(holder2, 1000, {from: holder1}); 123 | let _allowance1 = await poly 124 | .allowance 125 | .call(holder1, holder2); 126 | assert.strictEqual(_allowance1.toNumber(), 1000); 127 | await poly.transferFrom(holder1, holder3, 200, {from: holder2}); 128 | let _balance1 = await poly 129 | .balanceOf 130 | .call(holder3); 131 | assert.strictEqual(_balance1.toNumber(), 200); 132 | let _allowance2 = await poly 133 | .allowance 134 | .call(holder1, holder2); 135 | assert.strictEqual(_allowance2.toNumber(), 800); 136 | let _balance2 = await poly 137 | .balanceOf 138 | .call(holder1); 139 | assert.strictEqual(_balance2.toNumber(), 800); 140 | await poly.transferFrom(holder1, holder4, 200, {from: holder2}); 141 | let _balance3 = await poly 142 | .balanceOf 143 | .call(holder4); 144 | assert.strictEqual(_balance3.toNumber(), 200); 145 | let _allowance3 = await poly 146 | .allowance 147 | .call(holder1, holder2); 148 | assert.strictEqual(_allowance3.toNumber(), 600); 149 | let _balance4 = await poly 150 | .balanceOf 151 | .call(holder1); 152 | assert.strictEqual(_balance4.toNumber(), 600); 153 | }); 154 | 155 | it('Approve max (2^256 - 1)', async() => { 156 | let poly = await POLY.new(); 157 | await poly.approve(holder1, '115792089237316195423570985008687907853269984665640564039457584007913129639935', {from: holder2}); 158 | let _allowance = await poly.allowance(holder2, holder1); 159 | let result = _allowance.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e' + 160 | '+77'); 161 | assert.isTrue(result); 162 | }); 163 | 164 | 165 | it('approves: Holder1 approves Holder2 of 1000 & withdraws 800 & 500 (2nd tx should fail)', 166 | async() => { 167 | let poly = await POLY.new(); 168 | await poly.getTokens(100000,owner); 169 | await poly.transfer(holder1, 1000, {from: owner}); 170 | await poly.approve(holder2, 1000, {from: holder1}); 171 | let _allowance1 = await poly 172 | .allowance 173 | .call(holder1, holder2); 174 | assert.strictEqual(_allowance1.toNumber(), 1000); 175 | await poly.transferFrom(holder1, holder3, 800, {from: holder2}); 176 | let _balance1 = await poly 177 | .balanceOf 178 | .call(holder3); 179 | assert.strictEqual(_balance1.toNumber(), 800); 180 | let _allowance2 = await poly 181 | .allowance 182 | .call(holder1, holder2); 183 | assert.strictEqual(_allowance2.toNumber(), 200); 184 | let _balance2 = await poly 185 | .balanceOf 186 | .call(holder1); 187 | assert.strictEqual(_balance2.toNumber(), 200); 188 | try { 189 | await poly.transferFrom(holder1, holder3, 500, {from: holder2}); 190 | } catch (error) { 191 | ensureException(error); 192 | } 193 | }); 194 | }); 195 | 196 | describe("trasferFrom", async()=>{ 197 | it('transferFrom: Attempt to withdraw from account with no allowance -- fail', async() => { 198 | let poly = await POLY.new(); 199 | await poly.getTokens(100000,owner); 200 | await poly.transfer(holder1, 1000, {from: owner}); 201 | try { 202 | await poly 203 | .transferFrom 204 | .call(holder1, holder3, 100, {from: holder2}); 205 | } catch (error) { 206 | ensureException(error); 207 | } 208 | }); 209 | 210 | it('transferFrom: Allow holder2 1000 to withdraw from holder1. Withdraw 800 and then approve 0 & attempt transfer', 211 | async() => { 212 | let poly = await POLY.new(); 213 | await poly.getTokens(100000,owner); 214 | await poly.transfer(holder1, 1000, {from: owner}); 215 | await poly.approve(holder2, 1000, {from: holder1}); 216 | let _allowance1 = await poly 217 | .allowance 218 | .call(holder1, holder2); 219 | assert.strictEqual(_allowance1.toNumber(), 1000); 220 | await poly.transferFrom(holder1, holder3, 200, {from: holder2}); 221 | let _balance1 = await poly 222 | .balanceOf 223 | .call(holder3); 224 | assert.strictEqual(_balance1.toNumber(), 200); 225 | let _allowance2 = await poly 226 | .allowance 227 | .call(holder1, holder2); 228 | assert.strictEqual(_allowance2.toNumber(), 800); 229 | let _balance2 = await poly 230 | .balanceOf 231 | .call(holder1); 232 | assert.strictEqual(_balance2.toNumber(), 800); 233 | await poly.approve(holder2, 0, {from: holder1}); 234 | try { 235 | await poly.transferFrom(holder1, holder3, 200, {from: holder2}); 236 | } catch (error) { 237 | ensureException(error); 238 | } 239 | }); 240 | }); 241 | 242 | describe('events', async () => { 243 | it('should log Transfer event after transfer()', async () => { 244 | let poly = await POLY.new(); 245 | await poly.getTokens(100000,owner); 246 | let result = await poly.transfer(holder3, trasferFunds); 247 | 248 | assert.lengthOf(result.logs, 1); 249 | let event = result.logs[0]; 250 | assert.equal(event.event, 'Transfer'); 251 | assert.equal(event.args._from, owner); 252 | assert.equal(event.args._to, holder3); 253 | assert.equal(Number(event.args._value), trasferFunds); 254 | }); 255 | 256 | it('should log Transfer event after transferFrom()', async () => { 257 | let poly = await POLY.new(); 258 | await poly.getTokens(100000,owner); 259 | await poly.approve(holder1, allowedAmount,{ from : owner }); 260 | 261 | let value = allowedAmount / 2; 262 | let result = await poly.transferFrom(owner, holder2, value, { 263 | from: holder1, 264 | }); 265 | 266 | assert.lengthOf(result.logs, 1); 267 | let event = result.logs[0]; 268 | assert.equal(event.event, 'Transfer'); 269 | assert.equal(event.args._from, owner); 270 | assert.equal(event.args._to, holder2); 271 | assert.equal(Number(event.args._value), value); 272 | }); 273 | 274 | it('should log Approve event after approve()', async () => { 275 | let poly = await POLY.new(); 276 | await poly.getTokens(100000,owner); 277 | let result = await poly.approve(holder1, allowedAmount,{ from : owner }); 278 | 279 | assert.lengthOf(result.logs, 1); 280 | let event = result.logs[0]; 281 | assert.equal(event.event, 'Approval'); 282 | assert.equal(event.args._spender, holder1); 283 | assert.equal(Number(event.args._value), allowedAmount); 284 | }); 285 | }); 286 | 287 | }); 288 | 289 | 290 | -------------------------------------------------------------------------------- /test/SafeMath.js: -------------------------------------------------------------------------------- 1 | import expectRevert from './helpers/expectRevert'; 2 | const SafeMathMock = artifacts.require('./helpers/mockContracts/SafeMathMock.sol'); 3 | 4 | contract('SafeMath', (accounts) => { 5 | let safeMath; 6 | 7 | beforeEach(async () => { 8 | safeMath = await SafeMathMock.new(); 9 | }); 10 | 11 | describe('mul', async () => { 12 | [[5678, 1234], 13 | [2, 0], 14 | [575689, 123] 15 | ].forEach((pair) => { 16 | it(`multiplies ${pair[0]} and ${pair[1]} correctly`, async () => { 17 | let a = pair[0]; 18 | let b = pair[1]; 19 | await safeMath.multiply(a, b); 20 | let result = await safeMath.result(); 21 | assert.equal(result, a * b); 22 | }); 23 | }); 24 | 25 | it('should throw an error on multiplication overflow', async () => { 26 | let a = 115792089237316195423570985008687907853269984665640564039457584007913129639933; 27 | let b = 2; 28 | 29 | await expectRevert(safeMath.multiply(a, b)); 30 | }); 31 | }); 32 | 33 | describe('add', async () => { 34 | [[5678, 1234], 35 | [2, 0], 36 | [123, 575689] 37 | ].forEach((pair) => { 38 | it(`adds ${pair[0]} and ${pair[1]} correctly`, async () => { 39 | let a = pair[0]; 40 | let b = pair[1]; 41 | await safeMath.add(a, b); 42 | let result = await safeMath.result(); 43 | 44 | assert.equal(result, a + b); 45 | }); 46 | }); 47 | 48 | it('should throw an error on addition overflow', async () => { 49 | let a = 115792089237316195423570985008687907853269984665640564039457584007913129639935; 50 | let b = 1; 51 | 52 | await expectRevert(safeMath.add(a, b)); 53 | }); 54 | }); 55 | 56 | describe('sub', async () => { 57 | [[5678, 1234], 58 | [2, 0], 59 | [575689, 123] 60 | ].forEach((pair) => { 61 | it(`subtracts ${pair[0]} and ${pair[1]} correctly`, async () => { 62 | let a = pair[0]; 63 | let b = pair[1]; 64 | await safeMath.subtract(a, b); 65 | let result = await safeMath.result(); 66 | 67 | assert.equal(result, a - b); 68 | }); 69 | }); 70 | 71 | it('should throw an error if subtraction result would be negative', async () => { 72 | let a = 1234; 73 | let b = 5678; 74 | 75 | await expectRevert(safeMath.subtract(a, b)); 76 | }); 77 | }); 78 | 79 | describe('div', () => { 80 | [[5678, 1234], 81 | [2, 1], 82 | [123, 575689] 83 | ].forEach((pair) => { 84 | it(`divides ${pair[0]} and ${pair[1]} correctly`, async () => { 85 | let a = pair[0]; 86 | let b = pair[1]; 87 | await safeMath.divide(a, b); 88 | let result = await safeMath.result(); 89 | 90 | assert.equal(result, Math.floor(a / b)); 91 | }); 92 | }); 93 | 94 | it('should throw an error on division by 0', async () => { 95 | let a = 100; 96 | let b = 0; 97 | 98 | await expectRevert(safeMath.divide(a, b)); 99 | }); 100 | }); 101 | 102 | describe('max64', () => { 103 | [[5678, 1234], 104 | [2, 1], 105 | [123, 575689] 106 | ].forEach((pair) => { 107 | it(`get the max64 of ${pair[0]} and ${pair[1]} correctly`, async () => { 108 | let a = pair[0]; 109 | let b = pair[1]; 110 | await safeMath.max64(a, b); 111 | let result = await safeMath.result(); 112 | 113 | assert.equal(result, Math.max(a, b)); 114 | }); 115 | }); 116 | }); 117 | 118 | describe('min64', () => { 119 | [[5678, 1234], 120 | [2, 1], 121 | [123, 575689] 122 | ].forEach((pair) => { 123 | it(`get the min64 of ${pair[0]} and ${pair[1]} correctly`, async () => { 124 | let a = pair[0]; 125 | let b = pair[1]; 126 | await safeMath.min64(a, b); 127 | let result = await safeMath.result(); 128 | 129 | assert.equal(result, Math.min(a, b)); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('max256', () => { 135 | [[5678, 1234], 136 | [2, 1], 137 | [123, 575689] 138 | ].forEach((pair) => { 139 | it(`get the max256 of ${pair[0]} and ${pair[1]} correctly`, async () => { 140 | let a = pair[0]; 141 | let b = pair[1]; 142 | await safeMath.max256(a, b); 143 | let result = await safeMath.result(); 144 | 145 | assert.equal(result, Math.max(a, b)); 146 | }); 147 | }); 148 | }); 149 | 150 | describe('min256', () => { 151 | [[5678, 1234], 152 | [2, 1], 153 | [123, 575689] 154 | ].forEach((pair) => { 155 | it(`get the min256 of ${pair[0]} and ${pair[1]} correctly`, async () => { 156 | let a = pair[0]; 157 | let b = pair[1]; 158 | await safeMath.min256(a, b); 159 | let result = await safeMath.result(); 160 | 161 | assert.equal(result, Math.min(a, b)); 162 | }); 163 | }); 164 | }); 165 | }); 166 | -------------------------------------------------------------------------------- /test/SecurityTokenRegistrar.js: -------------------------------------------------------------------------------- 1 | import { convertHex, ensureException, duration } from './helpers/utils.js'; 2 | import latestTime from './helpers/latestTime'; 3 | 4 | const SecurityTokenRegistrar = artifacts.require('./SecurityTokenRegistrar.sol'); 5 | const SecurityToken = artifacts.require('./SecurityToken.sol'); 6 | const POLY = artifacts.require('./helpers/mockContracts/PolyTokenMock.sol'); 7 | const Compliance = artifacts.require('./Compliance.sol'); 8 | const Customers = artifacts.require('./Customers.sol'); 9 | 10 | 11 | contract('SecurityTokenRegistrar', accounts => { 12 | 13 | //createSecurityToken variables 14 | const name = 'Polymath Inc.'; 15 | const ticker = 'POLY'; 16 | const totalSupply = 1234567890; 17 | const maxPoly = 100000; 18 | const securityType = 5; 19 | const numberOfSecurityTypes = 8; //8 is chosen for testing, 20 | const nameSpaceMixed = "TestNameSpace"; 21 | const nameSpace = "testnamespace"; 22 | const nameSpaceFee = 10000; 23 | const nameSpaceOwner = accounts[6]; 24 | const quorum = 3; 25 | const lockupPeriod = latestTime() + duration.years(1); //Current time + 1 year is the locking period (Testing Only) 26 | const getAmount = 1000000; 27 | const approvedAmount = 10000; 28 | 29 | //accounts 30 | let owner = accounts[0]; 31 | let acct1 = accounts[1]; 32 | let acct2 = accounts[2]; 33 | let issuer2 = accounts[3]; 34 | let issuer1 = accounts[4]; 35 | let polyToken, polyCustomers, polyCompliance, STRegistrar; 36 | 37 | beforeEach(async () => { 38 | polyToken = await POLY.new(); 39 | polyCustomers = await Customers.new(polyToken.address); 40 | polyCompliance = await Compliance.new(polyCustomers.address); 41 | // Creation of the new SecurityTokenRegistrar contract 42 | STRegistrar = await SecurityTokenRegistrar.new( 43 | polyToken.address, 44 | polyCustomers.address, 45 | polyCompliance.address 46 | ); 47 | }) 48 | 49 | describe('Constructor', async () => { 50 | it('should have polyTokenAddress updated to contract storage', async () => { 51 | await polyCompliance.setRegistrarAddress(STRegistrar.address); 52 | let PTAddress = await STRegistrar.PolyToken.call(); 53 | assert.strictEqual(PTAddress, polyToken.address); 54 | }); 55 | }); 56 | 57 | describe('function createSecurityToken', async () => { 58 | it('should allow for the creation of a Security Token.', async () => { 59 | // Allowance Provided to SecurityToken Registrar contract 60 | await polyToken.getTokens(getAmount, issuer1, { from : issuer1 }); 61 | let issuerBalance = await polyToken.balanceOf(issuer1); 62 | assert.strictEqual(issuerBalance.toNumber(),getAmount); 63 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 64 | 65 | let allowedToken = await polyToken.allowance(issuer1, STRegistrar.address); 66 | assert.strictEqual(allowedToken.toNumber(),approvedAmount); 67 | // Create name space 68 | await STRegistrar.createNameSpace( 69 | nameSpaceMixed, 70 | nameSpaceFee, 71 | { 72 | from: nameSpaceOwner 73 | } 74 | ) 75 | 76 | // Creation of the Security Token 77 | let ST = await STRegistrar.createSecurityToken( 78 | nameSpace, 79 | name, 80 | ticker, 81 | totalSupply, 82 | 0, 83 | issuer1, 84 | numberOfSecurityTypes, 85 | { 86 | from: issuer1, 87 | }, 88 | ); 89 | let STAddress = await STRegistrar.getSecurityTokenAddress.call(nameSpace, ticker); 90 | assert.notEqual(STAddress, 0x0); 91 | let STData = await STRegistrar.getSecurityTokenData.call(STAddress); 92 | assert.strictEqual(STData[1], ticker); 93 | }); 94 | 95 | ////////////////////////////////////// 96 | //// createSecurityToken() Test Cases 97 | ////////////////////////////////////// 98 | 99 | describe('Creation of SecurityTokenMetaData Struct is within its proper limitations', async () => { 100 | it('createSecurityToken:should fail when total supply is zero', async () => { 101 | let totalSupply = 0; 102 | // Allowance Provided to SecurityToken Registrar contract 103 | await polyToken.getTokens(getAmount, issuer1, {from : issuer1 }); 104 | let issuerBalance = await polyToken.balanceOf(issuer1); 105 | assert.strictEqual(issuerBalance.toNumber(), getAmount); 106 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 107 | 108 | // Create name space 109 | await STRegistrar.createNameSpace( 110 | nameSpaceMixed, 111 | nameSpaceFee, 112 | { 113 | from: nameSpaceOwner 114 | } 115 | ) 116 | 117 | try{ 118 | await STRegistrar.createSecurityToken( 119 | nameSpace, 120 | name, 121 | ticker, 122 | totalSupply, 123 | 0, 124 | issuer1, 125 | numberOfSecurityTypes, 126 | { 127 | from : issuer1 128 | }) 129 | } catch(error) { 130 | ensureException(error); 131 | } 132 | }); 133 | 134 | it('createSecurityToken:should fail when name space does not exist', async () => { 135 | let totalSupply = 115792089237316195423570985008687907853269984665640564039457584007913129639936; 136 | // Allowance Provided to SecurityToken Registrar contract 137 | await polyToken.getTokens(getAmount, issuer1, { from : issuer1 }); 138 | let issuerBalance = await polyToken.balanceOf(issuer1); 139 | assert.strictEqual(issuerBalance.toNumber(), getAmount); 140 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 141 | 142 | try{ 143 | await STRegistrar.createSecurityToken( 144 | nameSpace, 145 | name, 146 | ticker, 147 | totalSupply, 148 | 0, 149 | issuer1, 150 | numberOfSecurityTypes, 151 | { 152 | from : issuer1 153 | }) 154 | } catch(error) { 155 | ensureException(error); 156 | } 157 | }); 158 | 159 | it('createSecurityToken:should fail when total supply is greater than (2^256)-1', async () => { 160 | let totalSupply = 115792089237316195423570985008687907853269984665640564039457584007913129639936; 161 | // Allowance Provided to SecurityToken Registrar contract 162 | await polyToken.getTokens(getAmount, issuer1, { from : issuer1 }); 163 | let issuerBalance = await polyToken.balanceOf(issuer1); 164 | assert.strictEqual(issuerBalance.toNumber(), getAmount); 165 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 166 | 167 | // Create name space 168 | await STRegistrar.createNameSpace( 169 | nameSpaceMixed, 170 | nameSpaceFee, 171 | { 172 | from:nameSpaceOwner 173 | } 174 | ) 175 | 176 | try{ 177 | await STRegistrar.createSecurityToken( 178 | nameSpace, 179 | name, 180 | ticker, 181 | totalSupply, 182 | 0, 183 | issuer1, 184 | numberOfSecurityTypes, 185 | { 186 | from : issuer1 187 | }) 188 | } catch(error) { 189 | ensureException(error); 190 | } 191 | }); 192 | 193 | it("createSecurityToken:should fail when ticker name is already exist", async () => { 194 | 195 | await polyToken.getTokens(getAmount, issuer1, { from : issuer1 }); 196 | await polyToken.getTokens(getAmount, issuer2, { from : issuer2 }); 197 | 198 | let issuerBalance1 = await polyToken.balanceOf(issuer1); 199 | let issuerBalance2 = await polyToken.balanceOf(issuer2); 200 | 201 | assert.strictEqual(issuerBalance1.toNumber(), getAmount); 202 | assert.strictEqual(issuerBalance2.toNumber(), getAmount); 203 | 204 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 205 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer2 }); 206 | 207 | let allowedToken1 = await polyToken.allowance(issuer1, STRegistrar.address); 208 | assert.strictEqual(allowedToken1.toNumber(), approvedAmount); 209 | 210 | let allowedToken2 = await polyToken.allowance(issuer2, STRegistrar.address); 211 | assert.strictEqual(allowedToken2.toNumber(), approvedAmount); 212 | 213 | // Create name space 214 | await STRegistrar.createNameSpace( 215 | nameSpaceMixed, 216 | nameSpaceFee, 217 | { 218 | from:nameSpaceOwner 219 | } 220 | ) 221 | 222 | let ST = await STRegistrar.createSecurityToken( 223 | nameSpace, 224 | name, 225 | ticker, 226 | totalSupply, 227 | 0, 228 | issuer1, 229 | numberOfSecurityTypes, 230 | { 231 | from : issuer1 232 | }); 233 | let STAddress = await STRegistrar.getSecurityTokenAddress.call(nameSpace, ticker); 234 | assert.notEqual(web3.eth.getCode(STAddress),0x0); 235 | try{ 236 | let ST = await STRegistrar.createSecurityToken( 237 | nameSpace, 238 | name, 239 | ticker, 240 | totalSupply, 241 | 0, 242 | issuer2, 243 | numberOfSecurityTypes, 244 | { 245 | from : issuer2 246 | }); 247 | } catch(error){ 248 | ensureException(error); 249 | } 250 | }); 251 | 252 | it('createSecurityToken:should fail when the approved quantity is less than the fee', async () => { 253 | await polyToken.getTokens(getAmount, issuer1, {from : issuer1 }); 254 | await polyToken.approve(STRegistrar.address, 1000, {from:issuer1}); 255 | 256 | // Create name space 257 | await STRegistrar.createNameSpace( 258 | nameSpaceMixed, 259 | nameSpaceFee, 260 | { 261 | from:nameSpaceOwner 262 | } 263 | ) 264 | 265 | try { 266 | let ST = await STRegistrar.createSecurityToken( 267 | nameSpace, 268 | name, 269 | ticker, 270 | totalSupply, 271 | 0, 272 | issuer1, 273 | numberOfSecurityTypes, 274 | { 275 | from: issuer1 276 | }); 277 | } catch(error) { 278 | ensureException(error); 279 | } 280 | }); 281 | 282 | it("createSecurityToken:should fail when the security registrar haven't approved to spent the poly" , async () => { 283 | await polyToken.getTokens(getAmount, issuer1, {from : issuer1}); 284 | 285 | // Create name space 286 | await STRegistrar.createNameSpace( 287 | nameSpaceMixed, 288 | nameSpaceFee, 289 | { 290 | from:nameSpaceOwner 291 | } 292 | ) 293 | 294 | try { 295 | let ST = await STRegistrar.createSecurityToken( 296 | nameSpace, 297 | name, 298 | ticker, 299 | totalSupply, 300 | issuer1, 301 | 0, 302 | numberOfSecurityTypes, 303 | { 304 | from : issuer1 305 | }); 306 | } catch(error) { 307 | ensureException(error); 308 | } 309 | }); 310 | 311 | it("changeNameSpace: Should able to change the name space fee", async () => { 312 | // Create name space 313 | await STRegistrar.createNameSpace( 314 | nameSpaceMixed, 315 | nameSpaceFee, 316 | { 317 | from: nameSpaceOwner 318 | } 319 | ); 320 | //change name space fee 321 | await STRegistrar.changeNameSpace( 322 | nameSpaceMixed, 323 | 100, 324 | { 325 | from: nameSpaceOwner 326 | } 327 | ); 328 | let data = await STRegistrar.getNameSpaceData(nameSpaceMixed); 329 | assert.equal(data[1], 100); 330 | }); 331 | 332 | it("changeNameSpace: Create security token after changing the name space fee", async () => { 333 | // Allowance Provided to SecurityToken Registrar contract 334 | await polyToken.getTokens(getAmount, issuer1, { from : issuer1 }); 335 | let issuerBalance = await polyToken.balanceOf(issuer1); 336 | assert.strictEqual(issuerBalance.toNumber(),getAmount); 337 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 338 | 339 | let allowedToken = await polyToken.allowance(issuer1, STRegistrar.address); 340 | assert.strictEqual(allowedToken.toNumber(),approvedAmount); 341 | // Create name space 342 | await STRegistrar.createNameSpace( 343 | nameSpaceMixed, 344 | nameSpaceFee, 345 | { 346 | from: nameSpaceOwner 347 | } 348 | ) 349 | 350 | // Creation of the Security Token 351 | let ST = await STRegistrar.createSecurityToken( 352 | nameSpace, 353 | name, 354 | ticker, 355 | totalSupply, 356 | 0, 357 | issuer1, 358 | numberOfSecurityTypes, 359 | { 360 | from: issuer1, 361 | }, 362 | ); 363 | let STAddress = await STRegistrar.getSecurityTokenAddress.call(nameSpace, ticker); 364 | assert.notEqual(STAddress, 0x0); 365 | let STData = await STRegistrar.getSecurityTokenData.call(STAddress); 366 | assert.strictEqual(STData[1], ticker); 367 | 368 | //change name space fee 369 | await STRegistrar.changeNameSpace( 370 | nameSpaceMixed, 371 | 100, 372 | { 373 | from: nameSpaceOwner 374 | } 375 | ); 376 | let data = await STRegistrar.getNameSpaceData(nameSpaceMixed); 377 | assert.equal(data[1], 100); 378 | await polyToken.approve(STRegistrar.address, approvedAmount, { from : issuer1 }); 379 | // Creation of the Security Token 380 | await STRegistrar.createSecurityToken( 381 | nameSpace, 382 | name, 383 | "TICK", 384 | totalSupply, 385 | 0, 386 | issuer1, 387 | numberOfSecurityTypes, 388 | { 389 | from: issuer1, 390 | }, 391 | ); 392 | let STAddress_new = await STRegistrar.getSecurityTokenAddress.call(nameSpace, "TICK"); 393 | assert.notEqual(STAddress, 0x0); 394 | let STData_new = await STRegistrar.getSecurityTokenData.call(STAddress_new); 395 | assert.strictEqual(STData_new[1], "TICK"); 396 | }); 397 | }); 398 | }); 399 | }); 400 | -------------------------------------------------------------------------------- /test/helpers/expectRevert.js: -------------------------------------------------------------------------------- 1 | export default async (promise) => { 2 | try { 3 | await promise; 4 | } catch (error) { 5 | // TODO: Check jump destination to destinguish between a throw and an actual invalid jump. 6 | const invalidOpcode = error.message.search('invalid opcode') > -1; 7 | 8 | // TODO: When we contract A calls contract B, and B throws, instead of an 'invalid jump', we get an 'out of gas' 9 | // error. How do we distinguish this from an actual out of gas event? The testrpc log actually show an "invalid 10 | // jump" event). 11 | const outOfGas = error.message.search('out of gas') > -1; 12 | 13 | const revert = error.message.search('revert') > -1; 14 | 15 | assert(invalidOpcode || outOfGas || revert, `Expected throw, got ${error} instead`); 16 | 17 | return; 18 | } 19 | 20 | assert(false, "Expected throw wasn't received"); 21 | }; -------------------------------------------------------------------------------- /test/helpers/latestTime.js: -------------------------------------------------------------------------------- 1 | // Returns the time of the last mined block in seconds 2 | export default function latestTime () { 3 | return web3.eth.getBlock('latest').timestamp; 4 | } 5 | -------------------------------------------------------------------------------- /test/helpers/mockContracts/PolyTokenMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../../../contracts/SafeMath.sol'; 4 | import '../../../contracts/interfaces/IERC20.sol'; 5 | 6 | /* 7 | POLY token faucet is only used on testnet for testing purposes 8 | !!!! NOT INTENDED TO BE USED ON MAINNET !!! 9 | */ 10 | 11 | contract PolyTokenMock is IERC20 { 12 | 13 | using SafeMath for uint256; 14 | uint256 public totalSupply = 1000000; 15 | string public name = "Polymath Network"; 16 | uint8 public decimals = 18; 17 | string public symbol = "POLY"; 18 | 19 | mapping(address => uint256) balances; 20 | mapping(address => mapping(address => uint256)) allowed; 21 | 22 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 23 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 24 | 25 | /* Token faucet - Not part of the ERC20 standard */ 26 | function getTokens(uint256 _amount, address _recipient) public returns (bool) { 27 | balances[_recipient] += _amount; 28 | totalSupply += _amount; 29 | return true; 30 | } 31 | 32 | /** 33 | * @dev send `_value` token to `_to` from `msg.sender` 34 | * @param _to The address of the recipient 35 | * @param _value The amount of token to be transferred 36 | * @return Whether the transfer was successful or not 37 | */ 38 | function transfer(address _to, uint256 _value) public returns (bool) { 39 | balances[msg.sender] = balances[msg.sender].sub(_value); 40 | balances[_to] = balances[_to].add(_value); 41 | Transfer(msg.sender, _to, _value); 42 | return true; 43 | } 44 | 45 | /** 46 | * @dev send `_value` token to `_to` from `_from` on the condition it is approved by `_from` 47 | * @param _from The address of the sender 48 | * @param _to The address of the recipient 49 | * @param _value The amount of token to be transferred 50 | * @return Whether the transfer was successful or not 51 | */ 52 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 53 | require(_to != address(0)); 54 | require(_value <= balances[_from]); 55 | require(_value <= allowed[_from][msg.sender]); 56 | 57 | balances[_from] = balances[_from].sub(_value); 58 | balances[_to] = balances[_to].add(_value); 59 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 60 | Transfer(_from, _to, _value); 61 | return true; 62 | } 63 | 64 | /** 65 | * @dev `balanceOf` function to get the balance of token holders 66 | * @param _owner The address from which the balance will be retrieved 67 | * @return The balance 68 | */ 69 | function balanceOf(address _owner) public view returns (uint256 balance) { 70 | return balances[_owner]; 71 | } 72 | 73 | /** 74 | * @dev `msg.sender` approves `_spender` to spend `_value` tokens 75 | * @param _spender The address of the account able to transfer the tokens 76 | * @param _value The amount of tokens to be approved for transfer 77 | * @return Whether the approval was successful or not 78 | */ 79 | function approve(address _spender, uint256 _value) public returns (bool) { 80 | allowed[msg.sender][_spender] = _value; 81 | Approval(msg.sender, _spender, _value); 82 | return true; 83 | } 84 | 85 | /** 86 | * @param _owner The address of the account owning tokens 87 | * @param _spender The address of the account able to transfer the tokens 88 | * @return Amount of remaining tokens allowed to spent 89 | */ 90 | function allowance(address _owner, address _spender) public view returns (uint256 remaining) { 91 | return allowed[_owner][_spender]; 92 | } 93 | 94 | function totalSupply() public view returns (uint256) { 95 | return totalSupply; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /test/helpers/mockContracts/SafeMathMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../../../contracts/SafeMath.sol'; 4 | 5 | contract SafeMathMock { 6 | uint public result; 7 | 8 | function multiply(uint a, uint b) public { 9 | result = SafeMath.mul(a, b); 10 | } 11 | 12 | function subtract(uint a, uint b) public { 13 | result = SafeMath.sub(a, b); 14 | } 15 | 16 | function add(uint a, uint b) public { 17 | result = SafeMath.add(a, b); 18 | } 19 | 20 | function divide(uint a, uint b) public { 21 | result = SafeMath.div(a, b); 22 | } 23 | 24 | function max64(uint64 a, uint64 b) public { 25 | result = SafeMath.max64(a, b); 26 | } 27 | 28 | function min64(uint64 a, uint64 b) public { 29 | result = SafeMath.min64(a, b); 30 | } 31 | 32 | function max256(uint256 a, uint256 b) public { 33 | result = SafeMath.max256(a, b); 34 | } 35 | 36 | function min256(uint256 a, uint256 b) public { 37 | result = SafeMath.min256(a, b); 38 | } 39 | } -------------------------------------------------------------------------------- /test/helpers/signData.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | const utils = ethers.utils; 3 | const ethUtil = require('ethereumjs-util'); 4 | 5 | function web3StringToBytes32(text) { 6 | var result = ethers.utils.hexlify(ethers.utils.toUtf8Bytes(text)); 7 | while (result.length < 66) { result += '0'; } 8 | if (result.length !== 66) { throw new Error("invalid web3 implicit bytes32"); } 9 | return result; 10 | } 11 | 12 | function signData(customerAddress, kycAddress, jurisdiction, division, role, accredited, nonce, pk) { 13 | // let nonce = 1;//Number(Math.random().toString().slice(2)); 14 | let accreditedBytes = "0x00"; 15 | if (accredited) { 16 | accreditedBytes = "0x01"; 17 | } 18 | // let nonce = 1; 19 | let packedData = utils.solidityKeccak256( 20 | [ "address", "address", "bytes32", "bytes32", "uint8", "bytes1", "uint" ], 21 | [ customerAddress, kycAddress, web3StringToBytes32(jurisdiction), web3StringToBytes32(division), role, accreditedBytes, nonce ] 22 | ).slice(2); 23 | packedData = new Buffer(packedData, 'hex'); 24 | packedData = Buffer.concat([ 25 | new Buffer(`\x19Ethereum Signed Message:\n${packedData.length.toString()}`), 26 | packedData]); 27 | packedData = web3.sha3(`0x${packedData.toString('hex')}`, { encoding: 'hex' }); 28 | return ethUtil.ecsign( 29 | new Buffer(packedData.slice(2), 'hex'), 30 | new Buffer(pk, 'hex')); 31 | } 32 | 33 | module.exports = { 34 | web3StringToBytes32, signData 35 | } 36 | -------------------------------------------------------------------------------- /test/helpers/testprivateKey.js: -------------------------------------------------------------------------------- 1 | export const pk = { 2 | account_0 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200', 3 | account_1 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201', 4 | account_2 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202', 5 | account_3 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203', 6 | account_4 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204', 7 | account_5 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205', 8 | account_6 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206', 9 | account_7 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207', 10 | account_8 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208', 11 | account_9 :'2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209' 12 | } 13 | 14 | export default { pk }; -------------------------------------------------------------------------------- /test/helpers/time.js: -------------------------------------------------------------------------------- 1 | // Special non-standard methods implemented by testrpc that 2 | // aren’t included within the original RPC specification. 3 | // See https://github.com/ethereumjs/testrpc#implemented-methods 4 | 5 | function increaseTime (duration) { 6 | const id = Date.now(); 7 | 8 | return new Promise((resolve, reject) => { 9 | web3.currentProvider.sendAsync({ 10 | jsonrpc: '2.0', 11 | method: 'evm_increaseTime', 12 | params: [duration], 13 | id: id, 14 | }, err1 => { 15 | if (err1) return reject(err1); 16 | 17 | web3.currentProvider.sendAsync({ 18 | jsonrpc: '2.0', 19 | method: 'evm_mine', 20 | id: id + 1, 21 | }, (err2, res) => { 22 | return err2 ? reject(err2) : resolve(res); 23 | }); 24 | }); 25 | }); 26 | } 27 | 28 | export default function takeSnapshot() { 29 | return new Promise((resolve, reject) => { 30 | web3.currentProvider.sendAsync({ 31 | jsonrpc: '2.0', 32 | method: 'evm_snapshot', 33 | params: [], 34 | id: new Date().getTime() 35 | }, (err, result) => { 36 | if (err) { 37 | return reject(err); 38 | } 39 | 40 | resolve(result.result); 41 | }); 42 | }); 43 | }; 44 | 45 | function revertToSnapshot(snapShotId) { 46 | return new Promise((resolve, reject) => { 47 | web3.currentProvider.sendAsync({ 48 | jsonrpc: '2.0', 49 | method: 'evm_revert', 50 | params: [snapShotId], 51 | id: new Date().getTime() 52 | }, (err) => { 53 | if (err) { 54 | return reject(err); 55 | } 56 | 57 | resolve(); 58 | }); 59 | }); 60 | }; 61 | 62 | export { increaseTime, takeSnapshot, revertToSnapshot }; -------------------------------------------------------------------------------- /test/helpers/utils.js: -------------------------------------------------------------------------------- 1 | /* global assert */ 2 | 3 | function isException(error) { 4 | let strError = error.toString(); 5 | return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert'); 6 | } 7 | 8 | function ensureException(error) { 9 | assert(isException(error), error.toString()); 10 | } 11 | 12 | async function timeDifference(timestamp1,timestamp2) { 13 | var difference = timestamp1 - timestamp2; 14 | return difference; 15 | } 16 | 17 | function convertHex(hexx) { 18 | var hex = hexx.toString(); //force conversion 19 | var str = ''; 20 | for (var i = 0; i < hex.length; i += 2) { 21 | let char = String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 22 | if (char != '\u0000') str += char; 23 | } 24 | return str; 25 | } 26 | 27 | export { 28 | ensureException, 29 | timeDifference, 30 | convertHex } 31 | 32 | export const duration = { 33 | seconds: function (val) { return val; }, 34 | minutes: function (val) { return val * this.seconds(60); }, 35 | hours: function (val) { return val * this.minutes(60); }, 36 | days: function (val) { return val * this.hours(24); }, 37 | weeks: function (val) { return val * this.days(7); }, 38 | years: function (val) { return val * this.days(365); }, 39 | }; 40 | 41 | 42 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('babel-polyfill'); 3 | //const WalletProvider = require("truffle-hdwallet-provider-privkey"); 4 | //const privKey = require('fs').readFileSync('./infura_privKey').toString(); 5 | //const apiKey = require('fs').readFileSync('./infura_apiKey').toString(); 6 | 7 | const WalletProvider = require("truffle-wallet-provider"); 8 | const keystore = require('fs').readFileSync('./sample-keystore').toString(); 9 | const pass = require('fs').readFileSync('./sample-pass').toString(); 10 | const wallet = require('ethereumjs-wallet').fromV3(keystore, pass); 11 | 12 | const config = { 13 | networks: { 14 | development: { 15 | host: 'localhost', 16 | port: 8545, 17 | gas: 15e6, 18 | gasPrice: 0x01, 19 | network_id: '*', 20 | }, 21 | ropsten: { 22 | //provider: new WalletProvider(privKey, "https://ropsten.infura.io/"+ apiKey), 23 | provider: new WalletProvider(wallet, "https://ropsten.infura.io/"), 24 | network_id: 3, 25 | gas: 4700036, 26 | gasPrice: 130000000000, 27 | }, 28 | coverage: { 29 | host: 'localhost', 30 | network_id: '*', 31 | port: 8555, 32 | gas: 0xfffffffffff, 33 | gasPrice: 0x01, 34 | }, 35 | }, 36 | mocha: { 37 | useColors: true, 38 | slow: 30000, 39 | bail: true, 40 | }, 41 | dependencies: {}, 42 | solc: { 43 | optimizer: { 44 | enabled: true, 45 | runs: 200, 46 | }, 47 | }, 48 | }; 49 | 50 | module.exports = config; 51 | -------------------------------------------------------------------------------- /whitepaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolymathNetwork/polymath-core-deprecated/40aec71d728413ae2f9c022c252254ffadcc86e4/whitepaper.pdf --------------------------------------------------------------------------------