@@ -78,6 +78,7 @@ data SolConf = SolConf { _contractAddr :: Addr -- ^ Contract address to us
78
78
, _initialBalance :: Integer -- ^ Initial balance of deployer and senders
79
79
, _prefix :: Text -- ^ Function name prefix used to denote tests
80
80
, _solcArgs :: String -- ^ Args to pass to @solc@
81
+ , _solcLibs :: [String ] -- ^ List of libraries to load, in order.
81
82
, _quiet :: Bool -- ^ Suppress @solc@ output, errors, and warnings
82
83
}
83
84
makeLenses ''SolConf
@@ -87,21 +88,38 @@ contracts :: (MonadIO m, MonadThrow m, MonadReader x m, Has SolConf x) => FilePa
87
88
contracts fp = do
88
89
a <- view (hasLens . solcArgs)
89
90
q <- view (hasLens . quiet)
90
- pure (a, q) >>= liftIO . solc >>= (\ case
91
+ ls <- view (hasLens . solcLibs)
92
+ pure (a, q, ls) >>= liftIO . solc >>= (\ case
91
93
Nothing -> throwM CompileFailure
92
94
Just m -> pure . toList $ fst m) where
93
95
usual = [" --combined-json=bin-runtime,bin,srcmap,srcmap-runtime,abi,ast" , fp]
94
- solc (a, q) = do
96
+ solc (a, q, ls ) = do
95
97
stderr <- if q then UseHandle <$> openFile " /dev/null" WriteMode
96
98
else pure Inherit
97
99
readSolc =<< writeSystemTempFile " "
98
- =<< readCreateProcess (proc " solc" $ usual <> words a ) {std_err = stderr} " "
100
+ =<< readCreateProcess (proc " solc" $ usual <> words (a ++ linkLibraries ls) ) {std_err = stderr} " "
99
101
100
102
populateAddresses :: [Addr ] -> Integer -> VM -> VM
101
103
populateAddresses [] _ vm = vm
102
104
populateAddresses (a: as) b vm = populateAddresses as b (vm & set (env . EVM. contracts . at a) (Just account))
103
105
where account = initialContract (RuntimeCode mempty ) & set nonce 1 & set balance (w256 $ fromInteger b)
104
106
107
+ -- | Address to load the first library
108
+ addrLibrary :: Addr
109
+ addrLibrary = 0xff
110
+
111
+ -- | Load a list of solidity contracts as libraries
112
+ loadLibraries :: (MonadIO m , MonadThrow m , MonadReader x m , Has SolConf x )
113
+ => [SolcContract ] -> Addr -> Addr -> VM -> m VM
114
+ loadLibraries [] _ _ vm = return vm
115
+ loadLibraries (l: ls) la d vm = loadLibraries ls (la + 1 ) d =<< loadRest
116
+ where loadRest = execStateT (execTx $ Tx (Right $ l ^. creationCode) d la 0 ) vm
117
+
118
+ -- | Generate a string to use as argument in solc to link libraries starting from addrLibrary
119
+ linkLibraries :: [String ] -> String
120
+ linkLibraries [] = " "
121
+ linkLibraries ls = " --libraries " ++ concat (imap (\ i x -> concat [x, " :" , show $ addrLibrary + (toEnum i :: Addr ) , " ," ]) ls)
122
+
105
123
-- | Given an optional contract name and a list of 'SolcContract's, try to load the specified
106
124
-- contract, or, if not provided, the first contract in the list, into a 'VM' usable for Echidna
107
125
-- testing and extract an ABI and list of tests. Throws exceptions if anything returned doesn't look
@@ -114,23 +132,27 @@ loadSpecified name cs = let ensure l e = if l == mempty then throwM e else pure
114
132
c <- choose cs name
115
133
q <- view (hasLens . quiet)
116
134
liftIO $ do
117
- when (isNothing name && length cs > 1 ) $
135
+ when (isNothing name && length cs > 1 && not q ) $
118
136
putStrLn " Multiple contracts found in file, only analyzing the first"
119
137
unless q . putStrLn $ " Analyzing contract: " <> unpack (c ^. contractName)
120
138
121
139
-- Local variables
122
- (SolConf ca d ads b pref _ _) <- view hasLens
140
+ (SolConf ca d ads b pref _ libs _) <- view hasLens
123
141
let bc = c ^. creationCode
124
142
blank = populateAddresses (ads |> d) b (vmForEthrunCreation bc)
125
143
abi = liftM2 (,) (view methodName) (fmap snd . view methodInputs) <$> toList (c ^. abiMap)
126
144
(tests, funs) = partition (isPrefixOf pref . fst ) abi
127
145
146
+ -- Select libraries
147
+ ls <- mapM (choose cs . Just . pack) libs
148
+
128
149
-- Make sure everything is ready to use, then ship it
129
150
mapM_ (uncurry ensure) [(abi, NoFuncs ), (tests, NoTests ), (funs, OnlyTests )] -- ABI checks
130
151
ensure bc (NoBytecode $ c ^. contractName) -- Bytecode check
131
152
case find (not . null . snd ) tests of
132
153
Just (t,_) -> throwM $ TestArgsFound t -- Test args check
133
- Nothing -> (, funs, fst <$> tests) <$> execStateT (execTx $ Tx (Right bc) d ca 0 ) blank
154
+ Nothing -> loadLibraries ls addrLibrary d blank >>=
155
+ fmap (, funs, fst <$> tests) . execStateT (execTx $ Tx (Right bc) d ca 0 )
134
156
135
157
where choose [] _ = throwM NoContracts
136
158
choose (c: _) Nothing = return c
0 commit comments