Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9deb9ca
functions for signed transfer manager
comeonbuddy Oct 11, 2018
a5d9454
solidity hashing issues
comeonbuddy Oct 16, 2018
35f7134
ready with double prefix
comeonbuddy Oct 16, 2018
bb36e85
change to ethers.js sign api
comeonbuddy Oct 18, 2018
845ba87
rick fixed sign data
comeonbuddy Nov 13, 2018
207cf89
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
comeonbuddy Nov 13, 2018
ebcd869
fix all the requested changes and tested ok
comeonbuddy Nov 15, 2018
07f1b8f
updated logic in verify transfer function
comeonbuddy Nov 16, 2018
2abea31
Apply suggestions from code review
maxsam4 Nov 16, 2018
95c4b84
added data length check
comeonbuddy Nov 16, 2018
3ad887b
WIP saving changes, fixed issues with signed function
comeonbuddy Nov 20, 2018
a14644a
fixing test cases calling from STO
comeonbuddy Nov 21, 2018
aef3f09
fix merge conflict
comeonbuddy Nov 21, 2018
fb3e6ba
WIP - bugs calling ST
comeonbuddy Nov 21, 2018
1138ad6
WIP ST test
comeonbuddy Nov 21, 2018
1c1db2e
Fixed test case
maxsam4 Nov 22, 2018
8445dd7
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
comeonbuddy Nov 22, 2018
26ab8ed
Move to Experimental folder
adamdossa Nov 26, 2018
81f9918
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
adamdossa Nov 26, 2018
707d0b7
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
adamdossa Nov 27, 2018
c35f699
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
adamdossa Nov 27, 2018
07fdbb2
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
maxsam4 Dec 11, 2018
f409386
Merge branch 'dev-2.1.0' into signed-transfer-manager-fixed
adamdossa Jan 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ module.exports = {
"semi": 0,
"no-undef": 0,
"key-spacing": 0,
"node":0,
"spaced-comment":0,
"node/no-deprecated-api":0
"no-tabs": 0,
"no-mixed-spaces-and-tabs":0
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
pragma solidity ^0.4.24;

import "../../TransferManager/ITransferManager.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
* @title Transfer Manager module for verifing transations with a signed message
*/
contract SignedTransferManager is ITransferManager {
using SafeMath for uint256;

bytes32 constant public ADMIN = "ADMIN";

//Keeps track of if the signature has been used or invalidated
mapping(bytes => bool) invalidSignatures;

//keep tracks of the address that allows to sign messages
mapping(address => bool) public signers;


// Emit when signer stats was changed
event UpdateSigners(address[] _signers, bool[] _signersStats);

// Emit when a signature has been deemed invalid
event InvalidSignature(bytes _data);


/**
* @notice Constructor
* @param _securityToken Address of the security token
* @param _polyAddress Address of the polytoken
*/
constructor (address _securityToken, address _polyAddress)
public
Module(_securityToken, _polyAddress)
{
}

/**
* @notice This function returns the signature of configure function
*/
function getInitFunction() public pure returns (bytes4) {
return bytes4(0);
}

/**
* @notice function to check if a signature is still valid
* @param _data signature
*/
function checkSignatureIsInvalid(bytes _data) public view returns(bool){
return invalidSignatures[_data];
}

/**
* @notice function to remove or add signer(s) onto the signer mapping
* @param _signers address array of signers
* @param _signersStats bool array of signers stats
*/
function updateSigners(address[] _signers, bool[] _signersStats) public withPerm(ADMIN) {
require(_signers.length == _signersStats.length, "input array length does not match");
for(uint256 i=0; i<_signers.length; i++){
signers[_signers[i]] = _signersStats[i];
}
emit UpdateSigners(_signers, _signersStats);
}

/**
* @notice allow verify transfer with signature
* @param _from address transfer from
* @param _to address transfer to
* @param _amount transfer amount
* @param _data signature
* @param _isTransfer bool value of isTransfer
* Sig needs to be valid (not used or deemed as invalid)
* Signer needs to be in the signers mapping
*/
function verifyTransfer(address _from, address _to, uint256 _amount, bytes _data , bool _isTransfer) public returns(Result) {
if (!paused) {

require (_isTransfer == false || msg.sender == securityToken, "Sender is not the owner");

// not using require to avoid revert in this function

if(_data.length == 0){
return Result.INVALID; // data input check
}

require(invalidSignatures[_data] != true, "Invalid signature - signature is either used or deemed as invalid");
bytes32 hash = keccak256(abi.encodePacked(this, _from, _to, _amount));
bytes32 prependedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
address signer = _recoverSignerAdd(prependedHash, _data);

if (signers[signer] != true){
return Result.NA;
} else if(_isTransfer == true) {
invalidSignatures[_data] = true;
return Result.VALID;
} else {
return Result.VALID;
}
}
return Result.NA;
}

/**
* @notice allow signers to deem a signature invalid
* @param _from address transfer from
* @param _to address transfer to
* @param _amount transfer amount
* @param _data signature
* Sig needs to be valid (not used or deemed as invalid)
* Signer needs to be in the signers mapping
*/
function invalidSignature(address _from, address _to, uint256 _amount, bytes _data) public {
require(signers[msg.sender] == true, "Only signer is allowed to invalid signature.");
require(invalidSignatures[_data] != true, "This signature is invalid.");

bytes32 hash = keccak256(abi.encodePacked(this, _from, _to, _amount));
bytes32 prependedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));

// return signer;
require(_recoverSignerAdd(prependedHash,_data) == msg.sender, "Incorrect Signer for this signature");

invalidSignatures[_data] = true;
emit InvalidSignature(_data);
}

/**
* @notice used to recover signers' add from signature
*/
function _recoverSignerAdd(bytes32 _hash, bytes _data) internal pure returns(address) {

//Check that the signature is valid
require(_data.length == 65, "Date input length is invalid");

bytes32 r;
bytes32 s;
uint8 v;

assembly {
r := mload(add(_data, 32))
s := mload(add(_data, 64))
v := and(mload(add(_data, 65)), 255)
}
if (v < 27) {
v += 27;
}
if (v != 27 && v != 28) {
return 0;
}

return ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), v, r, s);
}


/**
* @notice Return the permissions flag that are associated with ManualApproval transfer manager
*/
function getPermissions() public view returns(bytes32[]) {
bytes32[] memory allPermissions = new bytes32[](1);
allPermissions[0] = ADMIN;
return allPermissions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
pragma solidity ^0.4.24;

import "./SignedTransferManager.sol";
import "../../ModuleFactory.sol";

/**
* @title Factory for deploying SignedTransferManager module
*/
contract SignedTransferManagerFactory is ModuleFactory {

/**
* @notice Constructor
* @param _polyAddress Address of the polytoken
*/
constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public
ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost)
{
version = "1.0.0";
name = "SignedTransferManager";
title = "Signed Transfer Manager";
description = "Manage transfers using a signature";
compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
}


/**
* @notice used to launch the Module with the help of factory
* @return address Contract address of the Module
*/
function deploy(bytes /* _data */) external returns(address) {
if (setupCost > 0)
require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided");
address signedTransferManager = new SignedTransferManager(msg.sender, address(polyToken));
emit GenerateModuleFromFactory(address(signedTransferManager), getName(), address(this), msg.sender, setupCost, now);
return address(signedTransferManager);
}


/**
* @notice Type of the Module factory
*/
function getTypes() external view returns(uint8[]) {
uint8[] memory res = new uint8[](1);
res[0] = 2;
return res;
}

/**
* @notice Get the name of the Module
*/
function getName() public view returns(bytes32) {
return name;
}

/**
* @notice Get the description of the Module
*/
function getDescription() external view returns(string) {
return description;
}

/**
* @notice Get the version of the Module
*/
function getVersion() external view returns(string) {
return version;
}

/**
* @notice Get the title of the Module
*/
function getTitle() external view returns(string) {
return title;
}

/**
* @notice Get the setup cost of the module
*/
function getSetupCost() external view returns (uint256) {
return setupCost;
}

/**
* @notice Get the Instructions that helped to used the module
*/
function getInstructions() external view returns(string) {
return "Allows an issuer to maintain a list of signers who can validate transfer request using signatures. A mapping is used to track valid signers which can be managed by the issuer. verifytransfer function takes in a signature and if the signature is valid, it will verify the transfer. invalidSigature function allow the signer to make a signature invalid after it is signed. Init function takes no parameters.";
}

/**
* @notice Get the tags related to the module factory
*/
function getTags() public view returns(bytes32[]) {
bytes32[] memory availableTags = new bytes32[](2);
availableTags[0] = "General";
availableTags[1] = "Transfer Restriction";
return availableTags;
}


}
16 changes: 16 additions & 0 deletions test/helpers/createInstances.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ const PreSaleSTOFactory = artifacts.require("./PreSaleSTOFactory.sol");
const PolyToken = artifacts.require("./PolyToken.sol");
const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol");
const DummySTOFactory = artifacts.require("./DummySTOFactory.sol");
const SignedTransferManagerFactory = artifacts.require("./SignedTransferManagerFactory");
const MockBurnFactory = artifacts.require("./MockBurnFactory.sol");
const MockWrongTypeFactory = artifacts.require("./MockWrongTypeFactory.sol");
const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFactory.sol");
const VolumeRestrictionTM = artifacts.require("./VolumeRestrictionTM.sol");
const VestingEscrowWalletFactory = artifacts.require("./VestingEscrowWalletFactory.sol");
const VestingEscrowWallet = artifacts.require("./VestingEscrowWallet.sol");


const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port

Expand Down Expand Up @@ -80,6 +82,8 @@ let I_BlacklistTransferManagerFactory;
let I_VestingEscrowWalletLogic;
let I_STRProxied;
let I_MRProxied;
let I_SignedTransferManagerFactory;


// Initial fee for ticker registry and security token registry
const initRegFee = web3.utils.toWei("250");
Expand Down Expand Up @@ -312,6 +316,18 @@ export async function deployLockupVolumeRTMAndVerified(accountPolymath, MRProxyI
return new Array(I_VolumeRestrictionTransferManagerFactory);
}

export async function deploySignedTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) {
I_SignedTransferManagerFactory = await SignedTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath });
assert.notEqual(
I_SignedTransferManagerFactory.address.valueOf(),
"0x0000000000000000000000000000000000000000",
"SignedTransferManagerFactory contract was not deployed"
);

await registerAndVerifyByMR(I_SignedTransferManagerFactory.address, accountPolymath, MRProxyInstance);
return new Array(I_SignedTransferManagerFactory);
}

export async function deployScheduleCheckpointAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) {
I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath });
assert.notEqual(
Expand Down
18 changes: 17 additions & 1 deletion test/helpers/signData.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ethUtil = require("ethereumjs-util");

//this, _investor, _fromTime, _toTime, _validTo
function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, nonce, pk) {

let packedData = utils
.solidityKeccak256(
["address", "address", "uint256", "uint256", "uint256", "bool", "uint256", "uint256", "uint256"],
Expand All @@ -16,6 +17,21 @@ function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, rest
return ethUtil.ecsign(new Buffer(packedData.slice(2), "hex"), new Buffer(pk, "hex"));
}

// sign data for verify tranfer function
function signDataVerifyTransfer (tmAddress, fromAddress, toAddress, amount, account) {
let packedData = utils
.solidityKeccak256(
["address", "address", "address", "uint256"],
[tmAddress, fromAddress, toAddress, amount]
)
.slice(2);
packedData = new Buffer(packedData, "hex");
packedData = Buffer.concat([new Buffer(`\x19Ethereum Signed Message:\n${packedData.length.toString()}`), packedData]);
packedData = web3.sha3(`0x${packedData.toString("hex")}`, { encoding: "hex" });

return web3.eth.sign(account, packedData);
}

module.exports = {
signData
signData, signDataVerifyTransfer
};
Loading