2017-03-29 17:50:05 +00:00
let toposort = require ( 'toposort' ) ;
let async = require ( 'async' ) ;
2018-06-14 13:21:51 +00:00
const cloneDeep = require ( 'clone-deep' ) ;
2016-08-14 12:04:34 +00:00
2018-08-30 12:13:37 +00:00
const utils = require ( '../../utils/utils.js' ) ;
const fs = require ( '../../core/fs' ) ;
2017-02-19 18:17:28 +00:00
2016-09-28 01:04:40 +00:00
// TODO: create a contract object
2017-03-30 11:12:39 +00:00
class ContractsManager {
2018-08-24 13:25:47 +00:00
constructor ( embark , options ) {
2018-05-21 00:46:05 +00:00
const self = this ;
2018-08-07 22:44:48 +00:00
this . logger = embark . logger ;
this . events = embark . events ;
2017-03-30 11:12:39 +00:00
this . contracts = { } ;
this . contractDependencies = { } ;
2018-03-11 12:28:03 +00:00
this . deployOnlyOnConfig = false ;
2018-06-08 11:07:27 +00:00
this . compileError = false ;
2018-08-24 13:25:47 +00:00
this . compileOnceOnly = options . compileOnceOnly ;
2018-08-30 17:27:18 +00:00
this . disableOptimizations = options . disableOptimizations ;
2018-04-27 17:50:57 +00:00
2018-05-16 17:56:23 +00:00
self . events . setCommandHandler ( 'contracts:list' , ( cb ) => {
2018-06-08 11:07:27 +00:00
cb ( self . compileError , self . listContracts ( ) ) ;
2018-05-16 16:48:17 +00:00
} ) ;
2018-05-20 16:23:48 +00:00
2018-08-06 19:16:58 +00:00
self . events . setCommandHandler ( 'contracts:all' , ( cb ) => {
cb ( self . compileError , self . contracts ) ;
} ) ;
2018-07-30 18:33:01 +00:00
self . events . setCommandHandler ( 'contracts:dependencies' , ( cb ) => {
cb ( self . compileError , self . contractDependencies ) ;
} ) ;
2018-05-20 16:23:48 +00:00
self . events . setCommandHandler ( "contracts:contract" , ( contractName , cb ) => {
cb ( self . getContract ( contractName ) ) ;
} ) ;
2018-05-21 00:26:15 +00:00
2018-10-12 22:22:17 +00:00
self . events . setCommandHandler ( "contracts:contract:byTxHash" , ( txHash , cb ) => {
2018-10-29 13:15:48 +00:00
self . getContractByTxHash ( txHash , cb ) ;
2018-10-12 22:22:17 +00:00
} ) ;
2018-05-30 11:01:22 +00:00
self . events . setCommandHandler ( "contracts:build" , ( configOnly , cb ) => {
self . deployOnlyOnConfig = configOnly ; // temporary, should refactor
2018-06-08 11:07:27 +00:00
self . build ( ( err ) => {
cb ( err ) ;
2018-05-30 11:01:22 +00:00
} ) ;
} ) ;
2018-08-06 20:32:22 +00:00
self . events . setCommandHandler ( "contracts:reset:dependencies" , ( cb ) => {
self . contractDependencies = { } ;
cb ( ) ;
} ) ;
2018-05-21 00:26:15 +00:00
self . events . on ( "deploy:contract:error" , ( _contract ) => {
self . events . emit ( 'contractsState' , self . contractsState ( ) ) ;
} ) ;
self . events . on ( "deploy:contract:deployed" , ( _contract ) => {
self . events . emit ( 'contractsState' , self . contractsState ( ) ) ;
} ) ;
self . events . on ( "deploy:contract:undeployed" , ( _contract ) => {
self . events . emit ( 'contractsState' , self . contractsState ( ) ) ;
} ) ;
2018-05-30 11:01:22 +00:00
2018-07-12 13:02:16 +00:00
this . events . setCommandHandler ( 'setDashboardState' , ( ) => {
self . events . emit ( 'contractsState' , self . contractsState ( ) ) ;
} ) ;
2018-10-23 15:08:39 +00:00
self . events . setCommandHandler ( "contracts:formatted:all" , ( cb ) => {
2018-10-10 10:27:13 +00:00
const contracts = self . listContracts ( ) . map ( ( contract , index ) => (
{
2018-08-10 16:57:37 +00:00
className : contract . className ,
2018-07-12 13:02:16 +00:00
deploy : contract . deploy ,
error : contract . error ,
2018-08-27 02:49:01 +00:00
address : contract . deployedAddress ,
2018-10-10 10:27:13 +00:00
isFiddle : Boolean ( contract . isFiddle ) ,
args : contract . args ,
transactionHash : contract . transactionHash ,
gas : contract . gas ,
gasPrice : contract . gasPrice ,
index
}
) ) ;
cb ( contracts ) ;
2018-07-12 13:02:16 +00:00
} ) ;
2018-08-27 20:22:53 +00:00
embark . registerAPICall (
2018-07-12 13:02:16 +00:00
'get' ,
2018-10-12 11:33:48 +00:00
'/embark-api/contract/:contractName' ,
2018-07-12 13:02:16 +00:00
( req , res ) => {
2018-10-12 11:33:48 +00:00
self . events . request ( 'contracts:contract' , req . params . contractName , res . send . bind ( res ) ) ;
2018-07-12 13:02:16 +00:00
}
) ;
2018-08-27 20:22:53 +00:00
embark . registerAPICall (
2018-08-16 11:17:13 +00:00
'post' ,
2018-08-16 10:35:24 +00:00
'/embark-api/contract/:contractName/function' ,
( req , res ) => {
async . parallel ( {
contract : ( callback ) => {
2018-08-16 11:17:13 +00:00
self . events . request ( 'contracts:contract' , req . body . contractName , ( contract ) => callback ( null , contract ) ) ;
2018-08-16 10:35:24 +00:00
} ,
account : ( callback ) => {
self . events . request ( "blockchain:defaultAccount:get" , ( account ) => callback ( null , account ) ) ;
}
2018-08-16 15:18:07 +00:00
} , ( error , result ) => {
2018-08-16 13:13:36 +00:00
if ( error ) {
return res . send ( { error : error . message } ) ;
2018-08-16 10:35:24 +00:00
}
const { account , contract } = result ;
2018-08-16 11:17:13 +00:00
const abi = contract . abiDefinition . find ( definition => definition . name === req . body . method ) ;
2018-08-16 10:35:24 +00:00
const funcCall = ( abi . constant === true || abi . stateMutability === 'view' || abi . stateMutability === 'pure' ) ? 'call' : 'send' ;
2018-08-21 10:38:05 +00:00
2018-10-27 09:51:03 +00:00
self . events . request ( "blockchain:contract:create" , { abi : contract . abiDefinition , address : contract . deployedAddress } , async ( contractObj ) => {
2018-08-21 10:38:05 +00:00
try {
2018-10-27 09:51:03 +00:00
const gas = await contractObj . methods [ req . body . method ] . apply ( this , req . body . inputs ) . estimateGas ( ) ;
2018-10-28 16:10:38 +00:00
contractObj . methods [ req . body . method ] . apply ( this , req . body . inputs ) [ funcCall ] ( { from : account , gasPrice : req . body . gasPrice , gas : Math . floor ( gas ) } , ( error , result ) => {
2018-08-21 10:38:05 +00:00
if ( error ) {
return res . send ( { result : error . message } ) ;
}
res . send ( { result } ) ;
} ) ;
} catch ( e ) {
2018-10-28 16:10:38 +00:00
if ( funcCall === 'call' && e . message === 'Returned error: gas required exceeds allowance or always failing transaction' ) {
return res . send ( { result : 'Failing call, this could be because of invalid inputs or function guards that may have been triggered, or an unknown error.' } ) ;
}
2018-08-21 10:38:05 +00:00
res . send ( { result : e . message } ) ;
}
} ) ;
2018-08-16 15:18:07 +00:00
} ) ;
}
) ;
2018-08-27 20:22:53 +00:00
embark . registerAPICall (
2018-08-16 15:18:07 +00:00
'post' ,
'/embark-api/contract/:contractName/deploy' ,
( req , res ) => {
async . parallel ( {
contract : ( callback ) => {
self . events . request ( 'contracts:contract' , req . body . contractName , ( contract ) => callback ( null , contract ) ) ;
} ,
account : ( callback ) => {
self . events . request ( "blockchain:defaultAccount:get" , ( account ) => callback ( null , account ) ) ;
}
2018-08-21 10:38:05 +00:00
} , ( error , result ) => {
2018-08-16 15:18:07 +00:00
if ( error ) {
return res . send ( { error : error . message } ) ;
}
const { account , contract } = result ;
2018-08-21 10:38:05 +00:00
self . events . request ( "blockchain:contract:create" , { abi : contract . abiDefinition } , async ( contractObj ) => {
try {
const params = { data : ` 0x ${ contract . code } ` , arguments : req . body . inputs } ;
let gas = await contractObj . deploy ( params ) . estimateGas ( ) ;
2018-08-28 19:02:05 +00:00
let newContract = await contractObj . deploy ( params ) . send ( { from : account , gas , gasPrice : req . body . gasPrice } ) ;
2018-08-21 10:38:05 +00:00
res . send ( { result : newContract . _address } ) ;
} catch ( e ) {
res . send ( { result : e . message } ) ;
}
} ) ;
2018-08-16 10:35:24 +00:00
} ) ;
}
) ;
2018-08-27 20:22:53 +00:00
embark . registerAPICall (
2018-07-12 13:02:16 +00:00
'get' ,
2018-08-01 09:28:25 +00:00
'/embark-api/contracts' ,
2018-07-12 13:02:16 +00:00
( req , res ) => {
2018-10-12 11:33:48 +00:00
const result = [ ] ;
2018-10-23 15:08:39 +00:00
self . events . request ( 'contracts:formatted:all' , ( contracts ) => {
2018-10-12 11:33:48 +00:00
contracts . forEach ( ( contract ) => {
2018-10-15 12:06:19 +00:00
self . events . request ( 'contracts:contract' , contract . className , ( c ) => (
result . push ( Object . assign ( contract , c ) )
) ) ;
2018-10-12 11:33:48 +00:00
} ) ;
} ) ;
res . send ( result ) ;
2018-07-12 13:02:16 +00:00
}
) ;
2018-08-30 12:13:37 +00:00
2018-08-28 01:59:58 +00:00
embark . registerAPICall (
2018-08-17 08:17:14 +00:00
'post' ,
'/embark-api/contract/deploy' ,
( req , res ) => {
2018-08-24 11:08:05 +00:00
this . logger . trace ( ` POST request /embark-api/contract/deploy: \n ${ JSON . stringify ( req . body ) } ` ) ;
2018-08-31 05:10:18 +00:00
if ( typeof req . body . compiledContract !== 'object' ) {
return res . send ( { error : 'Body parameter \'compiledContract\' must be an object' } ) ;
}
2018-08-20 04:24:50 +00:00
self . compiledContracts = Object . assign ( self . compiledContracts , req . body . compiledContract ) ;
2018-08-24 11:08:05 +00:00
const contractNames = Object . keys ( req . body . compiledContract ) ;
2018-08-17 08:17:14 +00:00
self . build ( ( err , _mgr ) => {
2018-08-24 11:08:05 +00:00
if ( err ) {
return res . send ( { error : err . message } ) ;
}
2018-08-31 05:10:18 +00:00
// for each compiled contract, deploy (in parallel)
async . each ( contractNames , ( contractName , next ) => {
const contract = self . contracts [ contractName ] ;
2018-08-24 11:08:05 +00:00
contract . args = [ ] ; /* TODO: override contract.args */
contract . className = contractName ;
2018-08-27 02:49:01 +00:00
contract . isFiddle = true ;
2018-08-24 11:08:05 +00:00
self . events . request ( "deploy:contract" , contract , ( err ) => {
2018-08-27 02:49:01 +00:00
next ( err ) ;
2018-08-24 11:08:05 +00:00
} ) ;
} , ( err ) => {
let responseData = { } ;
if ( err ) {
responseData . error = err . message ;
}
else responseData . result = contractNames ;
2018-08-17 08:17:14 +00:00
this . logger . trace ( ` POST response /embark-api/contract/deploy: \n ${ JSON . stringify ( responseData ) } ` ) ;
res . send ( responseData ) ;
2018-08-24 11:08:05 +00:00
} ) ;
} , false , false ) ;
2018-08-17 08:17:14 +00:00
}
) ;
2016-10-31 01:31:29 +00:00
}
2017-03-30 11:12:39 +00:00
2018-10-29 14:33:12 +00:00
build ( done , _useContractFiles = true , resetContracts = true ) {
2017-03-30 11:12:39 +00:00
let self = this ;
2018-06-14 13:21:51 +00:00
self . contracts = { } ;
2018-08-30 17:27:18 +00:00
let compilerOptions = { disableOptimizations : this . disableOptimizations } ;
2018-08-24 11:08:05 +00:00
if ( resetContracts ) self . contracts = { } ;
2017-03-30 11:12:39 +00:00
async . waterfall ( [
2018-06-14 13:21:51 +00:00
function loadContractFiles ( callback ) {
self . events . request ( "config:contractsFiles" , ( contractsFiles ) => {
self . contractsFiles = contractsFiles ;
callback ( ) ;
} ) ;
} ,
function loadContractConfigs ( callback ) {
self . events . request ( "config:contractsConfig" , ( contractsConfig ) => {
self . contractsConfig = cloneDeep ( contractsConfig ) ;
callback ( ) ;
} ) ;
} ,
2017-03-30 11:12:39 +00:00
function compileContracts ( callback ) {
2018-06-08 11:07:27 +00:00
self . events . emit ( "status" , _ _ ( "Compiling..." ) ) ;
2018-08-24 13:25:47 +00:00
if ( self . compileOnceOnly && self . compiledContracts && Object . keys ( self . compiledContracts ) . length ) {
2018-06-04 14:45:50 +00:00
return callback ( ) ;
}
2018-08-30 17:27:18 +00:00
self . events . request ( "compiler:contracts" , self . contractsFiles , compilerOptions , function ( err , compiledObject ) {
2017-02-17 12:14:44 +00:00
self . compiledContracts = compiledObject ;
2017-02-28 13:03:03 +00:00
callback ( err ) ;
2017-02-17 12:14:44 +00:00
} ) ;
2017-03-30 11:12:39 +00:00
} ,
function prepareContractsFromConfig ( callback ) {
2018-06-08 11:07:27 +00:00
self . events . emit ( "status" , _ _ ( "Building..." ) ) ;
2018-08-30 12:13:37 +00:00
2018-08-24 11:08:05 +00:00
// if we are appending contracts (ie fiddle), we
// don't need to build a contract from config, so
// we can skip this entirely
if ( ! resetContracts ) return callback ( ) ;
2017-03-30 11:12:39 +00:00
let className , contract ;
for ( className in self . contractsConfig . contracts ) {
contract = self . contractsConfig . contracts [ className ] ;
2016-09-28 01:04:40 +00:00
2017-03-30 11:12:39 +00:00
contract . className = className ;
contract . args = contract . args || [ ] ;
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
self . contracts [ className ] = contract ;
}
callback ( ) ;
} ,
2018-06-14 19:22:50 +00:00
function getGasPriceForNetwork ( callback ) {
2018-07-17 12:10:22 +00:00
return callback ( null , self . contractsConfig . gasPrice ) ;
2018-06-14 19:22:50 +00:00
} ,
function prepareContractsFromCompilation ( gasPrice , callback ) {
2017-03-30 11:12:39 +00:00
let className , compiledContract , contractConfig , contract ;
for ( className in self . compiledContracts ) {
compiledContract = self . compiledContracts [ className ] ;
contractConfig = self . contractsConfig . contracts [ className ] ;
contract = self . contracts [ className ] || { className : className , args : [ ] } ;
contract . code = compiledContract . code ;
contract . runtimeBytecode = compiledContract . runtimeBytecode ;
2018-09-11 19:20:57 +00:00
contract . realRuntimeBytecode = ( compiledContract . realRuntimeBytecode || compiledContract . runtimeBytecode ) ;
2017-03-30 11:12:39 +00:00
contract . swarmHash = compiledContract . swarmHash ;
contract . gasEstimates = compiledContract . gasEstimates ;
contract . functionHashes = compiledContract . functionHashes ;
contract . abiDefinition = compiledContract . abiDefinition ;
2017-07-16 16:10:17 +00:00
contract . filename = compiledContract . filename ;
2018-04-17 21:51:36 +00:00
contract . originalFilename = compiledContract . originalFilename || ( "contracts/" + contract . filename ) ;
2018-08-30 12:13:37 +00:00
contract . path = fs . dappPath ( contract . originalFilename ) ;
2017-03-30 11:12:39 +00:00
contract . gas = ( contractConfig && contractConfig . gas ) || self . contractsConfig . gas || 'auto' ;
2018-06-14 19:22:50 +00:00
contract . gasPrice = contract . gasPrice || gasPrice ;
2017-03-30 11:12:39 +00:00
contract . type = 'file' ;
contract . className = className ;
self . contracts [ className ] = contract ;
}
callback ( ) ;
} ,
2017-12-14 21:16:51 +00:00
function setDeployIntention ( callback ) {
let className , contract ;
for ( className in self . contracts ) {
contract = self . contracts [ className ] ;
contract . deploy = ( contract . deploy === undefined ) || contract . deploy ;
2018-03-11 12:28:03 +00:00
if ( self . deployOnlyOnConfig && ! self . contractsConfig . contracts [ className ] ) {
contract . deploy = false ;
}
2017-12-14 21:16:51 +00:00
if ( contract . code === "" ) {
2018-08-07 20:21:01 +00:00
const message = _ _ ( "assuming %s to be an interface" , className ) ;
if ( contract . silent ) {
self . logger . trace ( message ) ;
} else {
self . logger . info ( message ) ;
}
2017-12-14 21:16:51 +00:00
contract . deploy = false ;
}
}
callback ( ) ;
} ,
2017-03-30 11:12:39 +00:00
/*eslint complexity: ["error", 11]*/
function dealWithSpecialConfigs ( callback ) {
let className , contract , parentContractName , parentContract ;
2017-12-19 19:07:48 +00:00
let dictionary = Object . keys ( self . contracts ) ;
2016-09-28 01:04:40 +00:00
2017-03-30 11:12:39 +00:00
for ( className in self . contracts ) {
contract = self . contracts [ className ] ;
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
if ( contract . instanceOf === undefined ) {
continue ;
}
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
parentContractName = contract . instanceOf ;
parentContract = self . contracts [ parentContractName ] ;
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
if ( parentContract === className ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( "%s : instanceOf is set to itself" , className ) ) ;
2017-03-30 11:12:39 +00:00
continue ;
}
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
if ( parentContract === undefined ) {
2018-08-16 10:35:24 +00:00
self . logger . error ( _ _ ( "{{className}}: couldn't find instanceOf contract {{parentContractName}}" , {
className : className ,
parentContractName : parentContractName
} ) ) ;
2017-12-20 14:41:12 +00:00
let suggestion = utils . proposeAlternative ( parentContractName , dictionary , [ className , parentContractName ] ) ;
2017-12-19 19:07:48 +00:00
if ( suggestion ) {
2018-05-08 21:49:46 +00:00
self . logger . warn ( _ _ ( 'did you mean "%s"?' , suggestion ) ) ;
2017-12-19 19:07:48 +00:00
}
2017-03-30 11:12:39 +00:00
continue ;
}
2016-09-28 01:04:40 +00:00
2017-03-30 11:12:39 +00:00
if ( parentContract . args && parentContract . args . length > 0 && ( ( contract . args && contract . args . length === 0 ) || contract . args === undefined ) ) {
contract . args = parentContract . args ;
}
2016-10-02 15:07:56 +00:00
2017-03-30 11:12:39 +00:00
if ( contract . code !== undefined ) {
2018-08-16 10:35:24 +00:00
self . logger . error ( _ _ ( "{{className}} has code associated to it but it's configured as an instanceOf {{parentContractName}}" , {
className : className ,
parentContractName : parentContractName
} ) ) ;
2017-03-30 11:12:39 +00:00
}
2016-09-28 01:04:40 +00:00
2017-03-30 11:12:39 +00:00
contract . code = parentContract . code ;
contract . runtimeBytecode = parentContract . runtimeBytecode ;
2018-09-20 01:55:00 +00:00
contract . realRuntimeBytecode = ( parentContract . realRuntimeBytecode || parentContract . runtimeBytecode ) ;
2017-03-30 11:12:39 +00:00
contract . gasEstimates = parentContract . gasEstimates ;
contract . functionHashes = parentContract . functionHashes ;
contract . abiDefinition = parentContract . abiDefinition ;
2016-10-31 00:48:16 +00:00
2017-03-30 11:12:39 +00:00
contract . gas = contract . gas || parentContract . gas ;
contract . gasPrice = contract . gasPrice || parentContract . gasPrice ;
contract . type = 'instance' ;
2016-09-28 01:04:40 +00:00
2016-10-29 16:02:07 +00:00
}
2017-03-30 11:12:39 +00:00
callback ( ) ;
} ,
function removeContractsWithNoCode ( callback ) {
let className , contract ;
2017-12-19 19:07:48 +00:00
let dictionary = Object . keys ( self . contracts ) ;
2017-03-30 11:12:39 +00:00
for ( className in self . contracts ) {
contract = self . contracts [ className ] ;
if ( contract . code === undefined ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( "%s has no code associated" , className ) ) ;
2017-12-19 19:07:48 +00:00
let suggestion = utils . proposeAlternative ( className , dictionary , [ className ] ) ;
if ( suggestion ) {
2018-05-08 21:49:46 +00:00
self . logger . warn ( _ _ ( 'did you mean "%s"?' , suggestion ) ) ;
2017-12-19 19:07:48 +00:00
}
2017-03-30 11:12:39 +00:00
delete self . contracts [ className ] ;
}
}
self . logger . trace ( self . contracts ) ;
callback ( ) ;
} ,
2018-03-05 01:07:39 +00:00
// TODO: needs refactoring, has gotten too complex
2018-10-29 14:33:12 +00:00
/*eslint complexity: ["error", 19]*/
/*eslint max-depth: ["error", 19]*/
2017-03-30 11:12:39 +00:00
function determineDependencies ( callback ) {
let className , contract ;
for ( className in self . contracts ) {
contract = self . contracts [ className ] ;
2017-07-16 17:31:40 +00:00
// look in code for dependencies
2018-07-12 14:23:24 +00:00
let libMatches = ( contract . code . match ( /:(.*?)(?=_)/g ) || [ ] ) ;
2017-07-16 17:31:40 +00:00
for ( let match of libMatches ) {
2018-08-16 10:35:24 +00:00
self . contractDependencies [ className ] = self . contractDependencies [ className ] || [ ] ;
self . contractDependencies [ className ] . push ( match . substr ( 1 ) ) ;
2017-07-16 17:31:40 +00:00
}
2017-03-30 11:12:39 +00:00
2017-07-16 17:31:40 +00:00
// look in arguments for dependencies
if ( contract . args === [ ] ) continue ;
2018-03-05 01:07:39 +00:00
let ref ;
if ( Array . isArray ( contract . args ) ) {
ref = contract . args ;
} else {
let keys = Object . keys ( contract . args ) ;
ref = keys . map ( ( k ) => contract . args [ k ] ) . filter ( ( x ) => ! x ) ;
}
2017-03-30 11:12:39 +00:00
for ( let j = 0 ; j < ref . length ; j ++ ) {
let arg = ref [ j ] ;
2018-08-14 17:36:08 +00:00
if ( arg [ 0 ] === "$" && ! arg . startsWith ( '$accounts' ) ) {
2017-03-30 11:12:39 +00:00
self . contractDependencies [ className ] = self . contractDependencies [ className ] || [ ] ;
self . contractDependencies [ className ] . push ( arg . substr ( 1 ) ) ;
2018-06-13 17:47:11 +00:00
self . checkDependency ( className , arg . substr ( 1 ) ) ;
2017-03-30 11:12:39 +00:00
}
2018-03-04 23:46:12 +00:00
if ( Array . isArray ( arg ) ) {
for ( let sub _arg of arg ) {
2018-08-14 17:36:08 +00:00
if ( sub _arg [ 0 ] === "$" && ! sub _arg . startsWith ( '$accounts' ) ) {
2018-03-04 23:46:12 +00:00
self . contractDependencies [ className ] = self . contractDependencies [ className ] || [ ] ;
self . contractDependencies [ className ] . push ( sub _arg . substr ( 1 ) ) ;
2018-06-13 17:47:11 +00:00
self . checkDependency ( className , sub _arg . substr ( 1 ) ) ;
2018-03-04 23:46:12 +00:00
}
}
}
2016-10-29 16:02:07 +00:00
}
2017-12-20 19:30:01 +00:00
// look in onDeploy for dependencies
2018-07-12 14:23:24 +00:00
if ( contract . onDeploy !== [ ] && contract . onDeploy !== undefined ) {
let regex = /\$\w+/g ;
contract . onDeploy . map ( ( cmd ) => {
2018-08-14 17:36:08 +00:00
if ( cmd . indexOf ( '$accounts' ) > - 1 ) {
return ;
}
2018-07-12 14:23:24 +00:00
cmd . replace ( regex , ( match ) => {
2018-10-25 23:11:49 +00:00
if ( match . substring ( 1 ) === contract . className ) {
// Contract self-referencing. In onDeploy, it should be available
return ;
}
2018-07-12 14:23:24 +00:00
self . contractDependencies [ className ] = self . contractDependencies [ className ] || [ ] ;
self . contractDependencies [ className ] . push ( match . substr ( 1 ) ) ;
} ) ;
} ) ;
}
// Remove duplicates
if ( self . contractDependencies [ className ] ) {
const o = { } ;
self . contractDependencies [ className ] . forEach ( function ( e ) {
o [ e ] = true ;
2017-12-20 19:30:01 +00:00
} ) ;
2018-07-12 14:23:24 +00:00
self . contractDependencies [ className ] = Object . keys ( o ) ;
}
2016-10-29 16:02:07 +00:00
}
2017-03-30 11:12:39 +00:00
callback ( ) ;
2016-10-29 16:02:07 +00:00
}
2018-06-20 15:15:47 +00:00
] , function ( err ) {
2017-03-30 11:12:39 +00:00
if ( err ) {
2018-06-08 11:07:27 +00:00
self . compileError = true ;
self . events . emit ( "status" , _ _ ( "Compile/Build error" ) ) ;
2018-10-01 20:55:45 +00:00
self . events . emit ( "outputError" , _ _ ( "Error building Dapp, please check console" ) ) ;
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( "Error Compiling/Building contracts: " ) + err ) ;
2018-08-16 10:35:24 +00:00
} else {
2018-06-15 03:23:31 +00:00
self . compileError = false ;
2017-03-30 11:12:39 +00:00
}
self . logger . trace ( "finished" . underline ) ;
done ( err , self ) ;
} ) ;
}
2018-06-13 17:47:11 +00:00
checkDependency ( className , dependencyName ) {
if ( ! this . contractDependencies [ className ] ) {
return ;
}
if ( ! this . contracts [ dependencyName ] ) {
this . logger . warn ( _ _ ( '{{className}} has a dependency on {{dependencyName}}' , { className , dependencyName } ) +
_ _ ( ', but it is not present in the contracts' ) ) ;
return ;
}
if ( ! this . contracts [ dependencyName ] . deploy ) {
this . logger . warn ( _ _ ( '{{className}} has a dependency on {{dependencyName}}' , { className , dependencyName } ) +
_ _ ( ', but it is not set to deploy. It could be an interface.' ) ) ;
}
}
2017-03-30 11:12:39 +00:00
getContract ( className ) {
return this . contracts [ className ] ;
2016-09-27 04:55:35 +00:00
}
2018-10-12 22:22:17 +00:00
getContractByTxHash ( txHash , cb ) {
this . events . request ( "blockchain:getTransaction" , txHash , ( err , tx ) => {
if ( err ) return cb ( err ) ;
for ( let contractName in this . contracts ) {
let contract = this . contracts [ contractName ] ;
if ( tx . to === contract . deployedAddress ) {
return cb ( null , contract ) ;
}
}
cb ( "no known contract found for txHash: " + txHash ) ;
} ) ;
}
2017-03-30 11:12:39 +00:00
sortContracts ( contractList ) {
let converted _dependencies = [ ] , i ;
for ( let contract in this . contractDependencies ) {
let dependencies = this . contractDependencies [ contract ] ;
for ( i = 0 ; i < dependencies . length ; i ++ ) {
converted _dependencies . push ( [ contract , dependencies [ i ] ] ) ;
}
}
2016-09-27 04:55:35 +00:00
2017-12-20 19:54:47 +00:00
let orderedDependencies ;
try {
2018-08-16 10:35:24 +00:00
orderedDependencies = toposort ( converted _dependencies . filter ( ( x ) => x [ 0 ] !== x [ 1 ] ) ) . reverse ( ) ;
} catch ( e ) {
2018-05-08 21:49:46 +00:00
this . logger . error ( ( _ _ ( "Error: " ) + e . message ) . red ) ;
this . logger . error ( _ _ ( "there are two or more contracts that depend on each other in a cyclic manner" ) . bold . red ) ;
this . logger . error ( _ _ ( "Embark couldn't determine which one to deploy first" ) . red ) ;
2017-12-20 19:58:59 +00:00
throw new Error ( "CyclicDependencyError" ) ;
//process.exit(0);
2017-12-20 19:54:47 +00:00
}
2016-09-27 04:55:35 +00:00
2018-08-02 19:17:40 +00:00
return contractList . sort ( function ( a , b ) {
2017-03-30 11:12:39 +00:00
let order _a = orderedDependencies . indexOf ( a . className ) ;
let order _b = orderedDependencies . indexOf ( b . className ) ;
return order _a - order _b ;
} ) ;
2016-08-14 12:04:34 +00:00
}
2017-03-30 11:12:39 +00:00
// TODO: should be built contracts
listContracts ( ) {
let contracts = [ ] ;
for ( let className in this . contracts ) {
let contract = this . contracts [ className ] ;
contracts . push ( contract ) ;
2016-10-21 03:31:42 +00:00
}
2017-03-30 11:12:39 +00:00
return this . sortContracts ( contracts ) ;
}
2016-10-21 03:31:42 +00:00
2017-03-30 11:12:39 +00:00
contractsState ( ) {
let data = [ ] ;
for ( let className in this . contracts ) {
let contract = this . contracts [ className ] ;
2018-08-07 20:18:40 +00:00
if ( contract . silent ) {
continue ;
}
2017-03-30 11:12:39 +00:00
let contractData ;
if ( contract . deploy === false ) {
contractData = [
className . green ,
2018-05-08 21:49:46 +00:00
_ _ ( 'Interface or set to not deploy' ) . green ,
2017-03-30 11:12:39 +00:00
"\t\tn/a" . green
] ;
} else if ( contract . error ) {
contractData = [
className . green ,
2017-12-14 20:03:08 +00:00
( contract . error ) . split ( "\n" ) [ 0 ] . replace ( /Error: /g , '' ) . substring ( 0 , 32 ) . red ,
2017-03-30 11:12:39 +00:00
'\t\tError' . red
] ;
} else {
contractData = [
className . green ,
( contract . deployedAddress || '...' ) . green ,
2018-05-08 21:49:46 +00:00
( ( contract . deployedAddress !== undefined ) ? ( "\t\t" + _ _ ( "Deployed" ) ) . green : ( "\t\t" + _ _ ( "Pending" ) ) . magenta )
2017-03-30 11:12:39 +00:00
] ;
}
data . push ( contractData ) ;
}
return data ;
2016-09-22 22:24:01 +00:00
}
2017-03-30 11:12:39 +00:00
}
2016-09-22 22:24:01 +00:00
2016-08-14 12:04:34 +00:00
module . exports = ContractsManager ;