From 7d9194c51addedd36becc7d8d8c1691fe6c4ba33 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 24 Jan 2018 15:37:24 +0530 Subject: [PATCH 1/2] fix #49 --- contracts/Compliance.sol | 34 ++++++++++++--------- contracts/SecurityToken.sol | 24 +++++++-------- contracts/Template.sol | 39 +++++++++++++++++++++---- contracts/interfaces/ICompliance.sol | 4 +-- contracts/interfaces/ISecurityToken.sol | 2 +- contracts/interfaces/ITemplate.sol | 8 ++++- test/SecurityToken.js | 7 +++-- test/Template.js | 3 +- 8 files changed, 81 insertions(+), 40 deletions(-) diff --git a/contracts/Compliance.sol b/contracts/Compliance.sol index 480c78c..a8bf30b 100644 --- a/contracts/Compliance.sol +++ b/contracts/Compliance.sol @@ -41,7 +41,7 @@ contract Compliance is ICompliance { address[] usedBy; } mapping(address => Offering) offerings; // Mapping used for storing the Offering detials corresponds to offering contract address - mapping(address => address[]) public offeringProposals; // Security token contract proposals for a specific security token + mapping(address => address[]) public offeringProposals; // Security token offering contract proposals for a specific security token Customers public PolyCustomers; // Instance of the Compliance contract uint256 public constant MINIMUM_VESTING_PERIOD = 60 * 60 * 24 * 100; // 100 Day minimum vesting period for POLY earned @@ -61,7 +61,7 @@ contract Compliance is ICompliance { * @param _offeringType The name of the security being issued * @param _issuerJurisdiction The jurisdiction id of the issuer * @param _accredited Accreditation status required for investors - * @param _KYC KYC provider used by the template + * @param _whiteListedKYC List of KYC provider used by the template * @param _details Details of the offering requirements * @param _expires Timestamp of when the template will expire * @param _fee Amount of POLY to use the template (held in escrow until issuance) @@ -72,7 +72,7 @@ contract Compliance is ICompliance { string _offeringType, bytes32 _issuerJurisdiction, bool _accredited, - address _KYC, + address[10] _whiteListedKYC, bytes32 _details, uint256 _expires, uint256 _fee, @@ -80,8 +80,9 @@ contract Compliance is ICompliance { uint256 _vestingPeriod ) public { - require(_KYC != address(0)); - var (,, role, verified, expires) = PolyCustomers.getCustomer(_KYC, msg.sender); + require(_whiteListedKYC[0] != address(0)); + // 0 index should be the provider addresss which verify the delegate or msg.sender + var (,, role, verified, expires) = PolyCustomers.getCustomer(_whiteListedKYC[0], msg.sender); require(role == 2 && verified && expires > now); require(_vestingPeriod >= MINIMUM_VESTING_PERIOD); address _template = new Template( @@ -89,7 +90,7 @@ contract Compliance is ICompliance { _offeringType, _issuerJurisdiction, _accredited, - _KYC, + _whiteListedKYC, _details, _expires, _fee, @@ -189,13 +190,18 @@ contract Compliance is ICompliance { ) public returns (bool success) { var (,,,,KYC) = ISecurityToken(_securityToken).getTokenDetails(); - var (,,, verified, expires) = PolyCustomers.getCustomer(KYC, offerings[_stoContract].auditor); - require(offerings[_stoContract].auditor == msg.sender); - require(verified); - require(expires > now); - offeringProposals[_securityToken].push(_stoContract); - LogNewContractProposal(_securityToken, _stoContract, msg.sender); - return true; + for (uint16 i; i < KYC.length; i++) { + var (,,, verified, expires) = PolyCustomers.getCustomer(KYC[i], offerings[_stoContract].auditor); + if (expires != 0) { + require(offerings[_stoContract].auditor == msg.sender); + require(verified); + require(expires > now); + offeringProposals[_securityToken].push(_stoContract); + LogNewContractProposal(_securityToken, _stoContract, msg.sender); + return true; + } + } + return false; } /** @@ -211,7 +217,7 @@ contract Compliance is ICompliance { { address proposedOffering = offeringProposals[_securityToken][_offeringProposalIndex]; require(offerings[proposedOffering].auditor == msg.sender); - var (,,,,chosenOffering) = ISecurityToken(_securityToken).getTokenDetails(); + var (,,,chosenOffering,) = ISecurityToken(_securityToken).getTokenDetails(); require(chosenOffering != proposedOffering); offeringProposals[_securityToken][_offeringProposalIndex] = address(0); return true; diff --git a/contracts/SecurityToken.sol b/contracts/SecurityToken.sol index 87c75a3..a7d8fd7 100644 --- a/contracts/SecurityToken.sol +++ b/contracts/SecurityToken.sol @@ -78,7 +78,7 @@ contract SecurityToken is IERC20 { uint256 public tokensIssuedBySTO = 0; // Flag variable to track the security token issued by the offering contract // Notifications - event LogTemplateSet(address indexed _delegateAddress, address _template, address indexed _KYC); + event LogTemplateSet(address indexed _delegateAddress, address _template); event LogUpdatedComplianceProof(bytes32 _merkleRoot, bytes32 _complianceProofHash); event LogSetSTOContract(address _STO, address indexed _STOtemplate, address indexed _auditor, uint256 _startTime, uint256 _endTime); event LogNewWhitelistedAddress(address _KYC, address _shareholder, uint8 _role); @@ -162,13 +162,12 @@ contract SecurityToken is IERC20 { address _template = PolyCompliance.getTemplateByProposal(this, _templateIndex); require(_template != address(0)); Template = ITemplate(_template); - var (_fee, _quorum, _vestingPeriod, _delegate, _KYC) = Template.getUsageDetails(); + var (_fee, _quorum, _vestingPeriod, _delegate,) = Template.getUsageDetails(); require(POLY.balanceOf(this) >= _fee); allocations[_delegate] = Allocation(_fee, _vestingPeriod, _quorum, 0, 0, false); delegate = _delegate; - KYC = _KYC; PolyCompliance.updateTemplateReputation(_template, _templateIndex); - LogTemplateSet(_delegate, _template, _KYC); + LogTemplateSet(_delegate, _template); return true; } @@ -233,16 +232,16 @@ contract SecurityToken is IERC20 { * The Issuer can add an address to the whitelist by themselves by * creating their own KYC provider and using it to verify the accounts * they want to add to the whitelist. - * @param _whitelistAddress Address attempting to join ST whitelist + * @param _KYC address to verify the msg.sender * @return bool success */ - function addToWhitelist(address _whitelistAddress) public returns (bool success) { - require(KYC == msg.sender || owner == msg.sender); - var (jurisdiction, accredited, role, verified, expires) = PolyCustomers.getCustomer(KYC, _whitelistAddress); + function addToWhitelist(address _KYC) public returns (bool success) { + require(Template.validKYC(_KYC)); + var (jurisdiction, accredited, role, verified, expires) = PolyCustomers.getCustomer(_KYC, msg.sender); require(verified && expires > now); require(Template.checkTemplateRequirements(jurisdiction, accredited, role)); - shareholders[_whitelistAddress] = Shareholder(msg.sender, true, role); - LogNewWhitelistedAddress(msg.sender, _whitelistAddress, role); + shareholders[msg.sender] = Shareholder(_KYC, true, role); + LogNewWhitelistedAddress(_KYC, msg.sender, role); return true; } @@ -319,8 +318,9 @@ contract SecurityToken is IERC20 { } // Get token details - function getTokenDetails() view public returns (address, address, bytes32, address, address) { - return (Template, delegate, merkleRoot, STO, KYC); + function getTokenDetails() view public returns (address, address, bytes32, address, address[10]) { + var (,,,,allowedKYC) = Template.getUsageDetails(); + return (Template, delegate, merkleRoot, STO, allowedKYC); } /////////////////////////////////////////////// Customized ERC20 Functions //////////////////////////////////////////////////////////// diff --git a/contracts/Template.sol b/contracts/Template.sol index 7f2f292..c645633 100644 --- a/contracts/Template.sol +++ b/contracts/Template.sol @@ -23,6 +23,8 @@ contract Template is ITemplate { bytes32 public issuerJurisdiction; // Variable contains the jurisdiction of the issuer of the template mapping(bytes32 => bool) public allowedJurisdictions; // Mapping that contains the allowed staus of Jurisdictions mapping(uint8 => bool) public allowedRoles; // Mapping that contains the allowed status of Roles + mapping(address => bool) public allowedKYC; // Mapping that contains the status of the kyc providers for this template + address[10] public allowedKYCProviders; // An array of addresses to store the allowed KYC providers of the template bool public accredited; // Variable that define the required level of accrediation for the investor address public KYC; // Address of the KYC provider bytes32 details; // Details of the offering requirements @@ -39,7 +41,8 @@ contract Template is ITemplate { string _offeringType, bytes32 _issuerJurisdiction, bool _accredited, - address _KYC, + address[10] _whiteListedKYC, + //address _KYC, bytes32 _details, uint256 _expires, uint256 _fee, @@ -47,7 +50,7 @@ contract Template is ITemplate { uint256 _vestingPeriod ) public { - require(_KYC != address(0) && _owner != address(0)); + require(_whiteListedKYC[0] != address(0) && _owner != address(0)); require(_fee > 0); require(_details.length > 0 && _expires > now && _issuerJurisdiction.length > 0); require(_quorum > 0 && _quorum <= 100); @@ -56,13 +59,28 @@ contract Template is ITemplate { offeringType = _offeringType; issuerJurisdiction = _issuerJurisdiction; accredited = _accredited; - KYC = _KYC; details = _details; finalized = false; expires = _expires; fee = _fee; quorum = _quorum; vestingPeriod = _vestingPeriod; + require(addAllowedKYC(_whiteListedKYC)); + + } + + /** + * @dev Internal function used to add whitelisted KYC providers in the template. + * @param _whiteListedKYC Array of permitted providers. + * @return bool + */ + + function addAllowedKYC(address[10] _whiteListedKYC) internal returns(bool) { + for (uint16 i; i < _whiteListedKYC.length; i++) { + allowedKYC[_whiteListedKYC[i]] = true; + allowedKYCProviders[i] = _whiteListedKYC[i]; + } + return true; } /** @@ -138,6 +156,15 @@ contract Template is ITemplate { return true; } + /** + * @dev check the authentication of the KYC addresses + * @param _KYC address need to check + */ + function validKYC(address _KYC) public returns (bool) { + return allowedKYC[_KYC]; + } + + /** * @dev getTemplateDetails is a constant function that gets template details * @return bytes32 details, bool finalized @@ -148,10 +175,10 @@ contract Template is ITemplate { } /** - * @dev `getUsageFees` is a function to get all the details on template usage fees + * @dev `getUsageDetails` is a function to get all the details on template usage fees * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC */ - function getUsageDetails() view public returns (uint256, uint8, uint256, address, address) { - return (fee, quorum, vestingPeriod, owner, KYC); + function getUsageDetails() view public returns (uint256, uint8, uint256, address, address[10]) { + return (fee, quorum, vestingPeriod, owner, allowedKYCProviders); } } diff --git a/contracts/interfaces/ICompliance.sol b/contracts/interfaces/ICompliance.sol index 77e85c4..d01b52d 100644 --- a/contracts/interfaces/ICompliance.sol +++ b/contracts/interfaces/ICompliance.sol @@ -14,7 +14,7 @@ interface ICompliance { * @param _offeringType The name of the security being issued * @param _issuerJurisdiction The jurisdiction id of the issuer * @param _accredited Accreditation status required for investors - * @param _KYC KYC provider used by the template + * @param _whiteListedKYC List of KYC provider used by the template * @param _details Details of the offering requirements * @param _expires Timestamp of when the template will expire * @param _fee Amount of POLY to use the template (held in escrow until issuance) @@ -25,7 +25,7 @@ interface ICompliance { string _offeringType, bytes32 _issuerJurisdiction, bool _accredited, - address _KYC, + address[10] _whiteListedKYC, bytes32 _details, uint256 _expires, uint256 _fee, diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index b570699..bac1fe4 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -91,7 +91,7 @@ function issueSecurityTokens(address _contributor, uint256 _amountOfSecurityTokens, uint256 _polyContributed) public returns (bool success); /// Get token details - function getTokenDetails() view public returns (address, address, bytes32, address, address); + function getTokenDetails() view public returns (address, address, bytes32, address, address[10]); /** * @dev Trasfer tokens from one address to another diff --git a/contracts/interfaces/ITemplate.sol b/contracts/interfaces/ITemplate.sol index abb1e1d..de764e5 100644 --- a/contracts/interfaces/ITemplate.sol +++ b/contracts/interfaces/ITemplate.sol @@ -41,6 +41,12 @@ interface ITemplate { uint8 _role ) public constant returns (bool allowed); + /** + * @dev check the authentication of the KYC addresses + * @param _KYC address need to check + */ + function validKYC(address _KYC) public returns (bool); + /** * @dev getTemplateDetails is a constant function that gets template details * @return bytes32 details, bool finalized @@ -51,5 +57,5 @@ interface ITemplate { * @dev `getUsageFees` is a function to get all the details on template usage fees * @return uint256 fee, uint8 quorum, uint256 vestingPeriod, address owner, address KYC */ - function getUsageDetails() view public returns (uint256, uint8, uint256, address, address); + function getUsageDetails() view public returns (uint256, uint8, uint256, address, address[10]); } diff --git a/test/SecurityToken.js b/test/SecurityToken.js index a85b1c0..345c9eb 100644 --- a/test/SecurityToken.js +++ b/test/SecurityToken.js @@ -78,6 +78,7 @@ contract('SecurityToken', accounts => { const kyc = 0x2fe38f0b394b297bc0d86ed6b66286572f5235f9; const details = 'going to launch on xx-xx-xx'; const fee = 1000; + let whiteListedKYC; // STO let mockStoContract = "0x81399dd18c7985a016eb2bb0a1f6aabf0745d667"; @@ -203,11 +204,13 @@ contract('SecurityToken', accounts => { STAddress = await STRegistrar.getSecurityTokenAddress.call(ticker); securityToken = await SecurityToken.at(STAddress); + whiteListedKYC = [provider0, provider1]; + let templateCreated = await compliance.createTemplate( offeringType, issuerJurisdiction, accredited, - provider0, + whiteListedKYC, details, expires, 1000, @@ -437,7 +440,7 @@ describe("Compliance contracts functions", async()=> { "Test", issuerJurisdiction, accredited, - provider0, + whiteListedKYC, "This is for Test", expires, 1000, diff --git a/test/Template.js b/test/Template.js index b07dc4b..f821fcd 100644 --- a/test/Template.js +++ b/test/Template.js @@ -20,7 +20,7 @@ contract("Template",(accounts)=>{ let owner; before(async()=>{ - KYCAddress = accounts[0]; // Asigining the KYC address + KYCAddress = [accounts[0], accounts[2], accounts[3]]; // Asigining the KYC address owner = accounts[1]; }); @@ -43,7 +43,6 @@ contract("Template",(accounts)=>{ assert.strictEqual(tempData[1].toNumber(), quorum); assert.strictEqual(tempData[2].toNumber(), vestingPeriod); assert.equal(tempData[3], owner); - assert.equal(tempData[4], KYCAddress); }); }); From fa8748edff8a689fb989a14e3d2237090087e0e6 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 24 Jan 2018 22:13:29 +0530 Subject: [PATCH 2/2] investor only be whitelist by the owner --- contracts/SecurityToken.sol | 13 ++++++++----- contracts/Template.sol | 1 - contracts/interfaces/ISecurityToken.sol | 8 ++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/contracts/SecurityToken.sol b/contracts/SecurityToken.sol index a7d8fd7..80aa0b3 100644 --- a/contracts/SecurityToken.sol +++ b/contracts/SecurityToken.sol @@ -232,16 +232,19 @@ contract SecurityToken is IERC20 { * The Issuer can add an address to the whitelist by themselves by * creating their own KYC provider and using it to verify the accounts * they want to add to the whitelist. - * @param _KYC address to verify the msg.sender + * @param _whitelistAddress Address attempting to join ST whitelist + * @param _KYC Address to verify the investor * @return bool success */ - function addToWhitelist(address _KYC) public returns (bool success) { + function addToWhitelist(address _whitelistedAddress, address _KYC) public returns (bool success) { + require(owner == msg.sender); + require(_whitelistedAddress != address(0)); require(Template.validKYC(_KYC)); - var (jurisdiction, accredited, role, verified, expires) = PolyCustomers.getCustomer(_KYC, msg.sender); + var (jurisdiction, accredited, role, verified, expires) = PolyCustomers.getCustomer(_KYC, _whitelistedAddress); require(verified && expires > now); require(Template.checkTemplateRequirements(jurisdiction, accredited, role)); - shareholders[msg.sender] = Shareholder(_KYC, true, role); - LogNewWhitelistedAddress(_KYC, msg.sender, role); + shareholders[_whitelistedAddress] = Shareholder(_KYC, true, role); + LogNewWhitelistedAddress(_KYC, _whitelistedAddress, role); return true; } diff --git a/contracts/Template.sol b/contracts/Template.sol index c645633..77c8321 100644 --- a/contracts/Template.sol +++ b/contracts/Template.sol @@ -42,7 +42,6 @@ contract Template is ITemplate { bytes32 _issuerJurisdiction, bool _accredited, address[10] _whiteListedKYC, - //address _KYC, bytes32 _details, uint256 _expires, uint256 _fee, diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index bac1fe4..a1ad242 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -63,10 +63,14 @@ /** * @dev Add a verified address to the Security Token whitelist - * @param _whitelistAddress Address attempting to join ST whitelist + * The Issuer can add an address to the whitelist by themselves by + * creating their own KYC provider and using it to verify the accounts + * they want to add to the whitelist. + * @param _whitelistAddress Address attempting to join ST whitelist + * @param _KYC Address to verify the investor * @return bool success */ - function addToWhitelist(uint8 KYCProviderIndex, address _whitelistAddress) public returns (bool success); + function addToWhitelist(address _whitelistedAddress, address _KYC) public returns (bool success); /** * @dev Allow POLY allocations to be withdrawn by owner, delegate, and the STO auditor at appropriate times