Flat .sol file generation utility
This commit is contained in:
commit
191f9d8264
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
.DS_Store
|
||||
out/
|
|
@ -0,0 +1,48 @@
|
|||
## Solidity flat file generation
|
||||
|
||||
##### Combines all local imports to one .sol file for any project's structure
|
||||
|
||||
##### Limitations:
|
||||
|
||||
1. All dependencies are locally in project
|
||||
2. Only imports like `import "filepath";` are supported
|
||||
|
||||
```
|
||||
git clone https://gitlab.com/blocknotary/oracles-combine-solidity
|
||||
cd oracles-combine-solidity
|
||||
npm install
|
||||
```
|
||||
|
||||
You can start script either
|
||||
|
||||
```
|
||||
npm start "path_to_not_flat_contract_definition_file.sol"
|
||||
```
|
||||
|
||||
or without paramaters (path to input file will be extracted from `./config.json`)
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
|
||||
|
||||
Expected result:
|
||||
|
||||
```
|
||||
Success! Flat file is generated to ./out directory
|
||||
```
|
||||
|
||||
`./flatContract.sol` - flat .sol file is created in output directory (`./out/` by default)
|
||||
|
||||
## Config
|
||||
|
||||
path `./config.json`
|
||||
|
||||
```
|
||||
{
|
||||
"inputFilePath": "./demo/src/Oracles.sol",
|
||||
"outputDir": "./out"
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"inputFilePath": "./demo/src/Oracles.sol",
|
||||
"outputDir": "./out"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract BallotClass {
|
||||
uint[] public ballots;
|
||||
uint public votingLowerLimit = 3;
|
||||
|
||||
struct Ballot {
|
||||
address owner;
|
||||
address miningKey;
|
||||
address affectedKey;
|
||||
string memo;
|
||||
uint affectedKeyType;
|
||||
uint createdAt;
|
||||
uint votingStart;
|
||||
uint votingDeadline;
|
||||
int votesAmmount;
|
||||
int result;
|
||||
bool addAction;
|
||||
bool active;
|
||||
mapping (address => bool) voted;
|
||||
}
|
||||
|
||||
mapping(uint => Ballot) public ballotsMapping;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract KeyClass {
|
||||
struct InitialKey {
|
||||
bool isNew;
|
||||
}
|
||||
|
||||
struct MiningKey {
|
||||
bool isActive;
|
||||
}
|
||||
|
||||
struct PayoutKey {
|
||||
bool isActive;
|
||||
}
|
||||
|
||||
struct VotingKey {
|
||||
bool isActive;
|
||||
}
|
||||
|
||||
mapping(address => MiningKey) public miningKeys;
|
||||
mapping(address => PayoutKey) public payoutKeys;
|
||||
mapping(address => VotingKey) public votingKeys;
|
||||
mapping(address => InitialKey) public initialKeys;
|
||||
mapping(address => address) public votingMiningKeysPair;
|
||||
mapping(address => address) public miningPayoutKeysPair;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract ValidatorClass {
|
||||
address[] public validators;
|
||||
address[] public disabledValidators;
|
||||
|
||||
struct Validator {
|
||||
string fullName;
|
||||
string streetName;
|
||||
string state;
|
||||
uint zip;
|
||||
uint licenseID;
|
||||
uint licenseExpiredAt;
|
||||
uint disablingDate;
|
||||
string disablingTX;
|
||||
}
|
||||
|
||||
mapping(address => Validator) public validator;
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./ValidatorsManager.sol";
|
||||
|
||||
contract BallotsManager is ValidatorsManager {
|
||||
/**
|
||||
@notice Adds new Ballot
|
||||
@param ballotID Ballot unique ID
|
||||
@param owner Voting key of notary, who creates ballot
|
||||
@param miningKey Mining key of notary, which is proposed to add or remove
|
||||
@param affectedKey Mining/payout/voting key of notary, which is proposed to add or remove
|
||||
@param affectedKeyType Type of affectedKey: 0 = mining key, 1 = voting key, 2 = payout key
|
||||
@param addAction Flag: adding is true, removing is false
|
||||
@param memo Ballot's memo
|
||||
*/
|
||||
function addBallot(
|
||||
uint ballotID,
|
||||
address owner,
|
||||
address miningKey,
|
||||
address affectedKey,
|
||||
uint affectedKeyType,
|
||||
bool addAction,
|
||||
string memo
|
||||
) {
|
||||
if (!checkVotingKeyValidity(msg.sender)) throw;
|
||||
if (licensesIssued == licensesLimit && addAction) throw;
|
||||
if (ballotsMapping[ballotID].createdAt > 0) throw;
|
||||
if (affectedKeyType == 0) {//mining key
|
||||
bool validatorIsAdded = false;
|
||||
for (uint i = 0; i < validators.length; i++) {
|
||||
if (validators[i] == affectedKey && addAction) throw; //validator is already added before
|
||||
if (validators[i] == affectedKey) {
|
||||
validatorIsAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (uint j = 0; j < disabledValidators.length; j++) {
|
||||
if (disabledValidators[j] == affectedKey) throw; //validator is already removed before
|
||||
}
|
||||
if (!validatorIsAdded && !addAction) throw; // no such validator in validators array to remove it
|
||||
} else if (affectedKeyType == 1) {//voting key
|
||||
if (checkVotingKeyValidity(affectedKey) && addAction) throw; //voting key is already added before
|
||||
if (!checkVotingKeyValidity(affectedKey) && !addAction) throw; //no such voting key to remove it
|
||||
} else if (affectedKeyType == 2) {//payout key
|
||||
if (checkPayoutKeyValidity(affectedKey) && addAction) throw; //payout key is already added before
|
||||
if (!checkPayoutKeyValidity(affectedKey) && !addAction) throw; //no such payout key to remove it
|
||||
}
|
||||
uint votingStart = now;
|
||||
ballotsMapping[ballotID] = Ballot({
|
||||
owner: owner,
|
||||
miningKey: miningKey,
|
||||
affectedKey: affectedKey,
|
||||
memo: memo,
|
||||
affectedKeyType: affectedKeyType,
|
||||
createdAt: now,
|
||||
votingStart: votingStart,
|
||||
votingDeadline: votingStart + 48 * 60 minutes,
|
||||
votesAmmount: 0,
|
||||
result: 0,
|
||||
addAction: addAction,
|
||||
active: true
|
||||
});
|
||||
ballots.push(ballotID);
|
||||
checkBallotsActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets active ballots' ids
|
||||
@return { "value" : "Array of active ballots ids" }
|
||||
*/
|
||||
function getBallots() constant returns (uint[] value) {
|
||||
return ballots;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's memo
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's memo" }
|
||||
*/
|
||||
function getBallotMemo(uint ballotID) constant returns (string value) {
|
||||
return ballotsMapping[ballotID].memo;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's action
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's action: adding is true, removing is false" }
|
||||
*/
|
||||
function getBallotAction(uint ballotID) constant returns (bool value) {
|
||||
return ballotsMapping[ballotID].addAction;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets mining key of notary
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Notary's mining key" }
|
||||
*/
|
||||
function getBallotMiningKey(uint ballotID) constant returns (address value) {
|
||||
return ballotsMapping[ballotID].miningKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets affected key of ballot
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's affected key" }
|
||||
*/
|
||||
function getBallotAffectedKey(uint ballotID) constant returns (address value) {
|
||||
return ballotsMapping[ballotID].affectedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets affected key type of ballot
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's affected key type" }
|
||||
*/
|
||||
function getBallotAffectedKeyType(uint ballotID) constant returns (uint value) {
|
||||
return ballotsMapping[ballotID].affectedKeyType;
|
||||
}
|
||||
|
||||
function toString(address x) internal returns (string) {
|
||||
bytes memory b = new bytes(20);
|
||||
for (uint i = 0; i < 20; i++)
|
||||
b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));
|
||||
return string(b);
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's owner full name
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's owner full name" }
|
||||
*/
|
||||
function getBallotOwner(uint ballotID) constant returns (string value) {
|
||||
address ballotOwnerVotingKey = ballotsMapping[ballotID].owner;
|
||||
address ballotOwnerMiningKey = votingMiningKeysPair[ballotOwnerVotingKey];
|
||||
string validatorFullName = validator[ballotOwnerMiningKey].fullName;
|
||||
bytes memory ownerName = bytes(validatorFullName);
|
||||
if (ownerName.length == 0)
|
||||
return toString(ballotOwnerMiningKey);
|
||||
else
|
||||
return validatorFullName;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's creation time
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's creation time" }
|
||||
*/
|
||||
function ballotCreatedAt(uint ballotID) constant returns (uint value) {
|
||||
return ballotsMapping[ballotID].createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's voting start date
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's voting start date" }
|
||||
*/
|
||||
function getBallotVotingStart(uint ballotID) constant returns (uint value) {
|
||||
return ballotsMapping[ballotID].votingStart;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's voting end date
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's voting end date" }
|
||||
*/
|
||||
function getBallotVotingEnd(uint ballotID) constant returns (uint value) {
|
||||
return ballotsMapping[ballotID].votingDeadline;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's amount of votes for
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's amount of votes for" }
|
||||
*/
|
||||
function getVotesFor(uint ballotID) constant returns (int value) {
|
||||
return (ballotsMapping[ballotID].votesAmmount + ballotsMapping[ballotID].result)/2;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets ballot's amount of votes against
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's amount of votes against" }
|
||||
*/
|
||||
function getVotesAgainst(uint ballotID) constant returns (int value) {
|
||||
return (ballotsMapping[ballotID].votesAmmount - ballotsMapping[ballotID].result)/2;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks, if ballot is active
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot's activity: active or not" }
|
||||
*/
|
||||
function ballotIsActive(uint ballotID) constant returns (bool value) {
|
||||
return ballotsMapping[ballotID].active;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks, if ballot is already voted by signer of current transaction
|
||||
@param ballotID Ballot unique ID
|
||||
@return { "value" : "Ballot is already voted by signer of current transaction: yes or no" }
|
||||
*/
|
||||
function ballotIsVoted(uint ballotID) constant returns (bool value) {
|
||||
return ballotsMapping[ballotID].voted[msg.sender];
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Votes
|
||||
@param ballotID Ballot unique ID
|
||||
@param accept Vote for is true, vote against is false
|
||||
*/
|
||||
function vote(uint ballotID, bool accept) {
|
||||
if (!checkVotingKeyValidity(msg.sender)) throw;
|
||||
Ballot v = ballotsMapping[ballotID];
|
||||
if (v.votingDeadline < now) throw;
|
||||
if (v.voted[msg.sender] == true) throw;
|
||||
v.voted[msg.sender] = true;
|
||||
v.votesAmmount++;
|
||||
if (accept) v.result++;
|
||||
else v.result--;
|
||||
checkBallotsActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Removes element by index from validators array and shift elements in array
|
||||
@param index Element's index to remove
|
||||
@return { "value" : "Updated validators array with removed element at index" }
|
||||
*/
|
||||
function removeValidator(uint index) internal returns(address[]) {
|
||||
if (index >= validators.length) return;
|
||||
|
||||
for (uint i = index; i<validators.length-1; i++){
|
||||
validators[i] = validators[i+1];
|
||||
}
|
||||
delete validators[validators.length-1];
|
||||
validators.length--;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks ballots' activity
|
||||
@dev Deactivate ballots, if ballot's time is finished and implement action: add or remove notary, if votes for are greater votes against, and total votes are greater than 3
|
||||
*/
|
||||
function checkBallotsActivity() internal {
|
||||
for (uint ijk = 0; ijk < ballots.length; ijk++) {
|
||||
Ballot b = ballotsMapping[ballots[ijk]];
|
||||
if (b.votingDeadline < now && b.active) {
|
||||
if ((int(b.votesAmmount) >= int(votingLowerLimit)) && b.result > 0) {
|
||||
if (b.addAction) { //add key
|
||||
if (b.affectedKeyType == 0) {//mining key
|
||||
if (licensesIssued < licensesLimit) {
|
||||
licensesIssued++;
|
||||
validators.push(b.affectedKey);
|
||||
}
|
||||
} else if (b.affectedKeyType == 1) {//voting key
|
||||
votingKeys[b.affectedKey] = VotingKey({isActive: true});
|
||||
votingMiningKeysPair[b.affectedKey] = b.miningKey;
|
||||
} else if (b.affectedKeyType == 2) {//payout key
|
||||
payoutKeys[b.affectedKey] = PayoutKey({isActive: true});
|
||||
miningPayoutKeysPair[b.miningKey] = b.affectedKey;
|
||||
}
|
||||
} else { //invalidate key
|
||||
if (b.affectedKeyType == 0) {//mining key
|
||||
for (uint jj = 0; jj < validators.length; jj++) {
|
||||
if (validators[jj] == b.affectedKey) {
|
||||
removeValidator(jj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
disabledValidators.push(b.affectedKey);
|
||||
validator[b.affectedKey].disablingDate = now;
|
||||
} else if (b.affectedKeyType == 1) {//voting key
|
||||
votingKeys[b.affectedKey] = VotingKey({isActive: false});
|
||||
} else if (b.affectedKeyType == 2) {//payout key
|
||||
payoutKeys[b.affectedKey] = PayoutKey({isActive: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
b.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./owned.sol";
|
||||
import "oracles-contract-key/KeyClass.sol";
|
||||
import "oracles-contract-validator/ValidatorClass.sol";
|
||||
import "oracles-contract-ballot/BallotClass.sol";
|
||||
|
||||
contract KeysManager is owned, KeyClass, ValidatorClass, BallotClass {
|
||||
int8 internal initialKeysIssued = 0;
|
||||
int8 internal initialKeysLimit = 12;
|
||||
int8 internal licensesIssued = 0;
|
||||
int8 internal licensesLimit = 52;
|
||||
|
||||
/**
|
||||
@notice Adds initial key
|
||||
@param key Initial key
|
||||
*/
|
||||
function addInitialKey(address key) onlyOwner {
|
||||
if (initialKeysIssued >= initialKeysLimit) throw;
|
||||
initialKeysIssued++;
|
||||
initialKeys[key] = InitialKey({isNew: true});
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Create production keys for notary
|
||||
@param miningAddr Mining key
|
||||
@param payoutAddr Payout key
|
||||
@param votingAddr Voting key
|
||||
*/
|
||||
function createKeys(
|
||||
address miningAddr,
|
||||
address payoutAddr,
|
||||
address votingAddr
|
||||
) {
|
||||
if (!checkInitialKey(msg.sender)) throw;
|
||||
//invalidate initial key
|
||||
delete initialKeys[msg.sender];
|
||||
miningKeys[miningAddr] = MiningKey({isActive: true});
|
||||
payoutKeys[payoutAddr] = PayoutKey({isActive: true});
|
||||
votingKeys[votingAddr] = VotingKey({isActive: true});
|
||||
//add mining key to list of validators
|
||||
licensesIssued++;
|
||||
validators.push(miningAddr);
|
||||
votingMiningKeysPair[votingAddr] = miningAddr;
|
||||
miningPayoutKeysPair[miningAddr] = payoutAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks, if initial key is new or not
|
||||
@param key Initial key
|
||||
@return { "value" : "Is initial key new or not new" }
|
||||
*/
|
||||
function checkInitialKey(address key) constant returns (bool value) {
|
||||
if (msg.sender != key) throw;
|
||||
return initialKeys[key].isNew;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks, if payout key is active or not
|
||||
@param addr Payout key
|
||||
@return { "value" : "Is payout key active or not active" }
|
||||
*/
|
||||
function checkPayoutKeyValidity(address addr) constant returns (bool value) {
|
||||
//if (msg.sender != addr) throw;
|
||||
return payoutKeys[addr].isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Checks, if voting key is active or not
|
||||
@param addr Voting key
|
||||
@return { "value" : "Is voting key active or not active" }
|
||||
*/
|
||||
function checkVotingKeyValidity(address addr) constant returns (bool value) {
|
||||
//if (msg.sender != addr) throw;
|
||||
return votingKeys[addr].isActive;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
import "./BallotsManager.sol";
|
||||
|
||||
/**
|
||||
@title Oracles Interface
|
||||
@author Oracles
|
||||
*/
|
||||
contract Oracles is BallotsManager {
|
||||
function Oracles() {
|
||||
validators.push(owner);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
import "oracles-contract-validator/ValidatorClass.sol" as ValidatorClass2;
|
||||
import "./KeysManager.sol";
|
||||
|
||||
contract ValidatorsManager is ValidatorClass, KeysManager {
|
||||
|
||||
/**
|
||||
@notice Adds new notary
|
||||
@param miningKey Notary's mining key
|
||||
@param zip Notary's zip code
|
||||
@param licenseID Notary's license ID
|
||||
@param licenseExpiredAt Notary's expiration date
|
||||
@param fullName Notary's full name
|
||||
@param streetName Notary's address
|
||||
@param state Notary's US state full name
|
||||
*/
|
||||
function addValidator(
|
||||
address miningKey,
|
||||
uint zip,
|
||||
uint licenseID,
|
||||
uint licenseExpiredAt,
|
||||
string fullName,
|
||||
string streetName,
|
||||
string state
|
||||
) {
|
||||
if (!checkVotingKeyValidity(msg.sender) && !checkInitialKey(msg.sender)) throw;
|
||||
if (licensesIssued == licensesLimit) throw;
|
||||
validator[miningKey] = ValidatorClass2.Validator({
|
||||
fullName: fullName,
|
||||
streetName: streetName,
|
||||
state: state,
|
||||
zip: zip,
|
||||
licenseID: licenseID,
|
||||
licenseExpiredAt: licenseExpiredAt,
|
||||
disablingDate: 0,
|
||||
disablingTX: ""
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets active notaries mining keys
|
||||
@return { "value" : "Array of active notaries mining keys" }
|
||||
*/
|
||||
function getValidators() constant returns (address[] value) {
|
||||
return validators;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets disabled notaries mining keys
|
||||
@return { "value" : "Array of disabled notaries mining keys" }
|
||||
*/
|
||||
function getDisabledValidators() constant returns (address[] value) {
|
||||
return disabledValidators;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's full name
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's full name" }
|
||||
*/
|
||||
function getValidatorFullName(address addr) constant returns (string value) {
|
||||
return validator[addr].fullName;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's address
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's address" }
|
||||
*/
|
||||
function getValidatorStreetName(address addr) constant returns (string value) {
|
||||
return validator[addr].streetName;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's state full name
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's state full name" }
|
||||
*/
|
||||
function getValidatorState(address addr) constant returns (string value) {
|
||||
return validator[addr].state;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's zip code
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's zip code" }
|
||||
*/
|
||||
function getValidatorZip(address addr) constant returns (uint value) {
|
||||
return validator[addr].zip;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's license ID
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's license ID" }
|
||||
*/
|
||||
function getValidatorLicenseID(address addr) constant returns (uint value) {
|
||||
return validator[addr].licenseID;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's license expiration date
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's license expiration date" }
|
||||
*/
|
||||
function getValidatorLicenseExpiredAt(address addr) constant returns (uint value) {
|
||||
return validator[addr].licenseExpiredAt;
|
||||
}
|
||||
|
||||
/**
|
||||
@notice Gets notary's disabling date
|
||||
@param addr Notary's mining key
|
||||
@return { "value" : "Notary's disabling date" }
|
||||
*/
|
||||
function getValidatorDisablingDate(address addr) constant returns (uint value) {
|
||||
return validator[addr].disablingDate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
contract owned {
|
||||
address public owner;
|
||||
|
||||
function owned() {
|
||||
owner = 0xDd0BB0e2a1594240fED0c2f2c17C1E9AB4F87126; //msg.sender
|
||||
}
|
||||
|
||||
modifier onlyOwner {
|
||||
if (msg.sender != owner) throw;
|
||||
_;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
const fs = require('fs');
|
||||
const glob = require("glob");
|
||||
const variables = require("./variables.js");
|
||||
const findUsingLibraryFor = require("./find-libraries-usage.js");
|
||||
|
||||
function addLibraries(parentDir, inputFileContent, srcFiles, cb) {
|
||||
let updatedFileContent = inputFileContent;
|
||||
let usingLibrariesFound = 0;
|
||||
findUsingLibraryFor(updatedFileContent, function(usingLibraries) {
|
||||
for (let k = 0; k < usingLibraries.length; k++) {
|
||||
let usingLibraryName = usingLibraries[k];
|
||||
for (let j = 0; j < srcFiles.length; j++) {
|
||||
let fileContent = fs.readFileSync(srcFiles[j], "utf8");
|
||||
if (fileContent.indexOf("library " + usingLibraryName) > -1) {
|
||||
if (variables.importedSrcFiles.indexOf(srcFiles[j]) === -1) {
|
||||
updatedFileContent = fileContent + updatedFileContent;
|
||||
srcFiles.splice(j,1);
|
||||
variables.importedSrcFiles.push(srcFiles[j]);
|
||||
usingLibrariesFound++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usingLibraries.length > usingLibrariesFound) {
|
||||
if (parentDir.lastIndexOf("/") > -1) {
|
||||
parentDir = parentDir.substring(0, parentDir.lastIndexOf("/"));
|
||||
glob(parentDir + "/**/*.sol", function(err, srcFiles) {
|
||||
addLibraries(parentDir, inputFileContent, srcFiles, cb);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cb(updatedFileContent);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = addLibraries;
|
|
@ -0,0 +1,57 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const findFile = require("./find-file.js");
|
||||
|
||||
function findAllImportPaths(dir, content, cb) {
|
||||
const subStr = "import ";
|
||||
let allImports = [];
|
||||
let regex = new RegExp(subStr,"gi");
|
||||
var importsCount = (content.match(regex) || []).length;
|
||||
let importsIterator = 0;
|
||||
while ( (result = regex.exec(content)) ) {
|
||||
let startImport = result.index;
|
||||
let endImport = startImport + content.substr(startImport).indexOf(";") + 1;
|
||||
let fullImportStatement = content.substring(startImport, endImport);
|
||||
let dependencyPath = fullImportStatement.split("\"").length > 1 ? fullImportStatement.split("\"")[1]: fullImportStatement.split("'")[1];
|
||||
let alias = fullImportStatement.split(" as ").length > 1?fullImportStatement.split(" as ")[1].split(";")[0]:null;
|
||||
let contractName;
|
||||
|
||||
importObj = {
|
||||
"startIndex": startImport,
|
||||
"endIndex": endImport,
|
||||
"dependencyPath": dependencyPath,
|
||||
"fullImportStatement": fullImportStatement,
|
||||
"alias": alias,
|
||||
"contractName": null
|
||||
};
|
||||
|
||||
if (alias) {
|
||||
alias = alias.replace(/\s/g,'');
|
||||
var fileExists = fs.existsSync(dependencyPath, fs.F_OK);
|
||||
if (fileExists) {
|
||||
importsIterator++;
|
||||
let fileContent = fs.readFileSync(dependencyPath, "utf8");
|
||||
if (fileContent.indexOf("contract ") > -1) {
|
||||
importObj.contractName = fileContent.substring((fileContent.indexOf("contract ") + ("contract ").length), fileContent.indexOf("{")).replace(/\s/g,'');
|
||||
}
|
||||
allImports.push(importObj);
|
||||
} else {
|
||||
findFile.byName(dir.substring(0, dir.lastIndexOf("/")), path.basename(dependencyPath), function(fileContent) {
|
||||
importsIterator++;
|
||||
if (fileContent.indexOf("contract ") > -1) {
|
||||
importObj.contractName = fileContent.substring((fileContent.indexOf("contract ") + ("contract ").length), fileContent.indexOf("{")).replace(/\s/g,'');
|
||||
}
|
||||
allImports.push(importObj);
|
||||
|
||||
if (importsIterator == importsCount) cb(allImports);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
importsIterator++;
|
||||
allImports.push(importObj);
|
||||
}
|
||||
}
|
||||
if (importsIterator == importsCount) cb(allImports);
|
||||
}
|
||||
|
||||
module.exports = findAllImportPaths;
|
|
@ -0,0 +1,61 @@
|
|||
const fs = require('fs');
|
||||
const glob = require("glob");
|
||||
const path = require("path");
|
||||
const variables = require("./variables.js");
|
||||
|
||||
function byName(dir, fileName, cb) {
|
||||
glob(dir + "/**/*.sol", function(err, srcFiles) {
|
||||
if (err) return console.log(err.message);
|
||||
|
||||
for (var j = 0; j < srcFiles.length; j++) {
|
||||
if (path.basename(srcFiles[j]) == fileName) {
|
||||
var fileContent = fs.readFileSync(srcFiles[j], "utf8");
|
||||
cb(fileContent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dir = dir.substring(0, dir.lastIndexOf("/"));
|
||||
byName(dir, fileName, cb);
|
||||
});
|
||||
}
|
||||
|
||||
function byNameAndReplace(dir, fileName, updatedFileContent, importStatement, cb) {
|
||||
glob(dir + "/**/*.sol", function(err, srcFiles) {
|
||||
if (err) return console.log(err.message);
|
||||
|
||||
var importIsReplacedBefore = false;
|
||||
for (var j = 0; j < srcFiles.length; j++) {
|
||||
if (path.basename(srcFiles[j]) == fileName) {
|
||||
if (variables.importedSrcFiles.indexOf(srcFiles[j]) === -1) {
|
||||
var fileContent = fs.readFileSync(srcFiles[j], "utf8");
|
||||
updatedFileContent = updatedFileContent.replace(importStatement, fileContent);
|
||||
variables.importedSrcFiles.push(srcFiles[j]);
|
||||
cb(updatedFileContent);
|
||||
return;
|
||||
} else {
|
||||
importIsReplacedBefore = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (importIsReplacedBefore) {
|
||||
updatedFileContent = updatedFileContent.replace(importStatement, "");
|
||||
cb(updatedFileContent);
|
||||
} else {
|
||||
if (dir.indexOf("/") > -1) {
|
||||
dir = dir.substring(0, dir.lastIndexOf("/"));
|
||||
byNameAndReplace(dir, fileName, updatedFileContent, importStatement, cb);
|
||||
} else {
|
||||
updatedFileContent = updatedFileContent.replace(importStatement, "");
|
||||
cb(updatedFileContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
byName: byName,
|
||||
byNameAndReplace: byNameAndReplace
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
function findUsingLibraryFor(content, cb) {
|
||||
const subStr = "using ";
|
||||
let usingLibraries = [];
|
||||
let regex = new RegExp(subStr,"gi");
|
||||
while ( (result = regex.exec(content)) ) {
|
||||
let startUsingLibraryFor = result.index;
|
||||
let endUsingLibraryFor = startUsingLibraryFor + content.substr(startUsingLibraryFor).indexOf(";") + 1;
|
||||
let dependencyPath = content.substring(startUsingLibraryFor, endUsingLibraryFor);
|
||||
dependencyPath = dependencyPath.split(subStr)[1].split("for")[0].replace(/\s/g,'');
|
||||
usingLibraries.indexOf(dependencyPath) === -1 ? usingLibraries.push(dependencyPath) : console.log("This item already exists");
|
||||
}
|
||||
cb(usingLibraries);
|
||||
}
|
||||
|
||||
module.exports = findUsingLibraryFor;
|
|
@ -0,0 +1,22 @@
|
|||
const removeTabs = require("./remove-tabs.js");
|
||||
|
||||
function removeDoubledSolidityVersion(content) {
|
||||
const subStr = "pragma solidity";
|
||||
//1st pragma solidity declaration
|
||||
let firstIndex = content.indexOf(subStr);
|
||||
let lastIndex = firstIndex + content.substr(firstIndex).indexOf(";") + 1;
|
||||
let contentPart = content.substr(lastIndex);
|
||||
let contentFiltered = contentPart;
|
||||
//remove other pragma solidity declarations
|
||||
let regex = new RegExp(subStr,"gi");
|
||||
while ( (result = regex.exec(contentPart)) ) {
|
||||
let start = result.index;
|
||||
let end = start + contentPart.substr(start).indexOf(";") + 1;
|
||||
if (start != firstIndex) contentFiltered = contentFiltered.replace(contentPart.substring(start, end), "");
|
||||
}
|
||||
let finalContent = content.substr(0, lastIndex) + contentFiltered;
|
||||
|
||||
return removeTabs(finalContent);
|
||||
}
|
||||
|
||||
module.exports = removeDoubledSolidityVersion;
|
|
@ -0,0 +1,6 @@
|
|||
function removeTabs(content) {
|
||||
content = content.replace(/[\r\n]+/g, '\n'); //removes tabs
|
||||
return content;
|
||||
}
|
||||
|
||||
module.exports = removeTabs;
|
|
@ -0,0 +1,43 @@
|
|||
const fs = require('fs');
|
||||
const path = require("path");
|
||||
const variables = require("./variables.js");
|
||||
const findFile = require("./find-file.js");
|
||||
const replaceRelativeImportPaths = require("./replace-relative-import-paths.js");
|
||||
const updateImportObjectLocationInTarget = require("./update-import-object-location-in-target.js");
|
||||
|
||||
function replaceAllImportsInCurrentLayer(i, importObjs, updatedFileContent, dir, cb) {
|
||||
if (i < importObjs.length) {
|
||||
var importObj = importObjs[i];
|
||||
|
||||
//replace contracts aliases
|
||||
if (importObj.contractName) {
|
||||
updatedFileContent = updatedFileContent.replace(importObj.alias + ".", importObj.contractName + ".");
|
||||
}
|
||||
|
||||
importObj = updateImportObjectLocationInTarget(importObj, updatedFileContent);
|
||||
var importStatement = updatedFileContent.substring(importObj.startIndex, importObj.endIndex);
|
||||
|
||||
var fileExists = fs.existsSync(dir + importObj.dependencyPath, fs.F_OK);
|
||||
if (fileExists) {
|
||||
var importedFileContent = fs.readFileSync(dir + importObj.dependencyPath, "utf8");
|
||||
replaceRelativeImportPaths(importedFileContent, path.dirname(importObj.dependencyPath) + "/", function(importedFileContentUpdated) {
|
||||
if (variables.importedSrcFiles.indexOf(path.basename(dir + importObj.dependencyPath)) === -1) {
|
||||
variables.importedSrcFiles.push(path.basename(dir + importObj.dependencyPath));
|
||||
updatedFileContent = updatedFileContent.replace(importStatement, importedFileContentUpdated);
|
||||
}
|
||||
else updatedFileContent = updatedFileContent.replace(importStatement, "");
|
||||
|
||||
i++;
|
||||
replaceAllImportsInCurrentLayer(i, importObjs, updatedFileContent, dir, cb);
|
||||
});
|
||||
} else {
|
||||
console.log("!!!" + importObj.dependencyPath + " SOURCE FILE NOT FOUND. TRY TO FIND IT RECURSIVELY!!!");
|
||||
findFile.byNameAndReplace(dir.substring(0, dir.lastIndexOf("/")), path.basename(importObj.dependencyPath), updatedFileContent, importStatement, function(_updatedFileContent) {
|
||||
i++;
|
||||
replaceAllImportsInCurrentLayer(i, importObjs, _updatedFileContent, dir, cb);
|
||||
});
|
||||
}
|
||||
} else cb(updatedFileContent);
|
||||
}
|
||||
|
||||
module.exports = replaceAllImportsInCurrentLayer;
|
|
@ -0,0 +1,16 @@
|
|||
const findAllImportPaths = require("./find-all-import-paths.js");
|
||||
const replaceAllImportsInCurrentLayer = require("./replace-all-imports-in-current-layer");
|
||||
|
||||
function replaceAllImportsRecursively(fileContent, dir, cb) {
|
||||
let updatedFileContent = fileContent;
|
||||
findAllImportPaths(dir, updatedFileContent, function(_importObjs) {
|
||||
if (!_importObjs) return cb(updatedFileContent);
|
||||
if (_importObjs.length == 0) return cb(updatedFileContent);
|
||||
|
||||
replaceAllImportsInCurrentLayer(0, _importObjs, updatedFileContent, dir, function(_updatedFileContent) {
|
||||
replaceAllImportsRecursively(_updatedFileContent, dir, cb);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = replaceAllImportsRecursively;
|
|
@ -0,0 +1,33 @@
|
|||
const updateImportObjectLocationInTarget = require("./update-import-object-location-in-target.js");
|
||||
const findAllImportPaths = require("./find-all-import-paths.js");
|
||||
|
||||
function replaceRelativeImportPaths(fileContent, curDir, cb) {
|
||||
let updatedFileContent = fileContent;
|
||||
findAllImportPaths(curDir, fileContent, function(importObjs) {
|
||||
if (!importObjs) return cb(updatedFileContent);
|
||||
if (importObjs.length == 0) return cb(updatedFileContent);
|
||||
|
||||
for (let j = 0; j < importObjs.length; j++) {
|
||||
let importObj = importObjs[j];
|
||||
|
||||
importObj = updateImportObjectLocationInTarget(importObj, updatedFileContent);
|
||||
let importStatement = updatedFileContent.substring(importObj.startIndex, importObj.endIndex);
|
||||
|
||||
let newPath;
|
||||
if (importObj.dependencyPath.indexOf("../") == 0) {
|
||||
newPath = curDir + importObj.dependencyPath;
|
||||
}
|
||||
else if (importObj.dependencyPath.indexOf("./") == 0) {
|
||||
newPath = curDir + importObj.dependencyPath;
|
||||
}
|
||||
else {
|
||||
newPath = importObj.dependencyPath;
|
||||
}
|
||||
let importStatementNew = importStatement.replace(importObj.dependencyPath, newPath);
|
||||
updatedFileContent = updatedFileContent.replace(importStatement, importStatementNew);
|
||||
}
|
||||
cb(updatedFileContent);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = replaceRelativeImportPaths;
|
|
@ -0,0 +1,9 @@
|
|||
function updateImportObjectLocationInTarget(importObj, content) {
|
||||
let startIndexNew = content.indexOf(importObj.fullImportStatement);
|
||||
let endIndexNew = startIndexNew - importObj.startIndex + importObj.endIndex;
|
||||
importObj.startIndex = startIndexNew;
|
||||
importObj.endIndex = endIndexNew;
|
||||
return importObj;
|
||||
}
|
||||
|
||||
module.exports = updateImportObjectLocationInTarget;
|
|
@ -0,0 +1,31 @@
|
|||
const path = require("path");
|
||||
const fs = require('fs');
|
||||
|
||||
const configPath = "./config.json";
|
||||
let configExists = fs.existsSync(configPath, fs.F_OK);
|
||||
let config;
|
||||
if (configExists) config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
||||
|
||||
//Input solidity file path
|
||||
let args = process.argv.slice(2);
|
||||
let inputFilePath = args.length > 0?args[0]:config.inputFilePath;
|
||||
//Input solidity file dir name
|
||||
let inputFileDir = path.dirname(inputFilePath);
|
||||
//Input parent dir
|
||||
let parentDir = inputFileDir;
|
||||
//Output directory to store flat combined solidity file
|
||||
let outDir = config.outputDir?config.outputDir:"./out";
|
||||
|
||||
let allSrcFiles = [];
|
||||
let importedSrcFiles = [];
|
||||
|
||||
|
||||
module.exports = {
|
||||
args: args,
|
||||
inputFilePath: inputFilePath,
|
||||
inputFileDir: inputFileDir,
|
||||
parentDir: parentDir,
|
||||
outDir: outDir,
|
||||
allSrcFiles: allSrcFiles,
|
||||
importedSrcFiles: importedSrcFiles
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
const fs = require('fs');
|
||||
const glob = require("glob");
|
||||
const path = require("path");
|
||||
const variables = require("./helpers/variables.js");
|
||||
const removeDoubledSolidityVersion = require("./helpers/remove-doubled-solidity-version.js");
|
||||
const replaceAllImportsRecursively = require("./helpers/replace-all-imports-recursively.js");
|
||||
const addLibraries = require("./helpers/add-libraries.js");
|
||||
|
||||
fs.readFile(variables.inputFilePath, "utf8", readInputFileCallBack);
|
||||
|
||||
function readInputFileCallBack(err, inputFileContent) {
|
||||
if (err) return console.log(err.message);
|
||||
|
||||
generateFlatFile(variables.parentDir + "/", variables.parentDir + "/**/*.sol", inputFileContent);
|
||||
}
|
||||
|
||||
function generateFlatFile(dir, path, inputFileContent) {
|
||||
glob(path, function(err, srcFiles) {
|
||||
variables.allSrcFiles = srcFiles;
|
||||
if (err) return console.log(err.message);
|
||||
getAllSolFilesCallBack(inputFileContent, dir, path, srcFiles);
|
||||
});
|
||||
}
|
||||
|
||||
function getAllSolFilesCallBack(inputFileContent, dir, path, srcFiles) {
|
||||
addLibraries(variables.parentDir, inputFileContent, variables.allSrcFiles, function(intermediateFileContent) {
|
||||
replaceAllImportsRecursively(inputFileContent, dir, function(outputFileContent) {
|
||||
outputFileContent = removeDoubledSolidityVersion(outputFileContent);
|
||||
if (!fs.existsSync(variables.outDir)) fs.mkdirSync(variables.outDir);
|
||||
fs.writeFileSync(variables.outDir + "/flatContract.sol", outputFileContent);
|
||||
console.log("Success! Flat file is generated to " + variables.outDir + " directory");
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "oracles-combine-solidity",
|
||||
"version": "1.0.0",
|
||||
"description": "Combine solidity files to one flat file",
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/oraclesorg/oracles-combine-solidity"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "oraclesorg",
|
||||
"license": "MIT",
|
||||
"homepage": "https://oracles.org/",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.2",
|
||||
"path": "^0.12.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "6.10.1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/oraclesorg/oracles-combine-solidity"
|
||||
},
|
||||
"main": "index.js",
|
||||
"devDependencies": {}
|
||||
}
|
Loading…
Reference in New Issue