Skip to content

Commit

Permalink
Support configuring Etherscan API key via config file (#1227)
Browse files Browse the repository at this point in the history
Partially fixes #1226
  • Loading branch information
elopez authored May 6, 2024
1 parent 5d55003 commit 7ff9173
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 9 deletions.
1 change: 1 addition & 0 deletions lib/Echidna/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ instance FromJSON EConfigWithUsage where
<*> (UIConf <$> v ..:? "timeout" <*> formatParser)
<*> v ..:? "rpcUrl"
<*> v ..:? "rpcBlock"
<*> v ..:? "etherscanApiKey"
where
useKey k = modify' $ Set.insert k
x ..:? k = useKey k >> lift (x .:? k)
Expand Down
13 changes: 9 additions & 4 deletions lib/Echidna/Onchain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ rpcBlockEnv = do
val <- lookupEnv "ECHIDNA_RPC_BLOCK"
pure (val >>= readMaybe)

etherscanApiKey :: IO (Maybe Text)
etherscanApiKey = do
val <- lookupEnv "ETHERSCAN_API_KEY"
pure (Text.pack <$> val)

-- TODO: temporary solution, handle errors gracefully
safeFetchContractFrom :: EVM.Fetch.BlockNumber -> Text -> Addr -> IO (Maybe Contract)
safeFetchContractFrom rpcBlock rpcUrl addr =
Expand Down Expand Up @@ -122,11 +127,11 @@ readFileIfExists path = do

-- | "Reverse engineer" the SolcContract and SourceCache structures for the
-- code fetched from the outside
externalSolcContract :: Addr -> Contract -> IO (Maybe (SourceCache, SolcContract))
externalSolcContract addr c = do
externalSolcContract :: Env -> Addr -> Contract -> IO (Maybe (SourceCache, SolcContract))
externalSolcContract env addr c = do
let runtimeCode = forceBuf $ fromJust $ view bytecode c
putStr $ "Fetching Solidity source for contract at address " <> show addr <> "... "
srcRet <- Etherscan.fetchContractSource addr
srcRet <- Etherscan.fetchContractSource env.cfg.etherscanApiKey addr
putStrLn $ if isJust srcRet then "Success!" else "Error!"
putStr $ "Fetching Solidity source map for contract at address " <> show addr <> "... "
srcmapRet <- Etherscan.fetchContractSourceMap addr
Expand Down Expand Up @@ -189,7 +194,7 @@ saveCoverageReport env runId = do
forM_ (Map.toList contractsCache) $ \(addr, mc) ->
case mc of
Just contract -> do
r <- externalSolcContract addr contract
r <- externalSolcContract env addr contract
case r of
Just (externalSourceCache, solcContract) -> do
let dir' = dir </> show addr
Expand Down
1 change: 1 addition & 0 deletions lib/Echidna/Types/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data EConfig = EConfig

, rpcUrl :: Maybe Text
, rpcBlock :: Maybe Word64
, etherscanApiKey :: Maybe Text
}

instance Read OutputFormat where
Expand Down
8 changes: 3 additions & 5 deletions lib/Etherscan.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Data.Sequence (Seq)
import Data.Text (Text)
import Data.Text qualified as T
import Network.HTTP.Simple (httpSink, parseRequest, getResponseBody, httpJSON)
import System.Environment (lookupEnv)
import Text.HTML.DOM (sinkDoc)
import Text.XML.Cursor (attributeIs, content, element, fromDocument, ($//), (&//))

Expand All @@ -23,14 +22,13 @@ data SourceCode = SourceCode
}
deriving Show

fetchContractSource :: Addr -> IO (Maybe SourceCode)
fetchContractSource addr = do
apiKey <- lookupEnv "ETHERSCAN_API_KEY"
fetchContractSource :: Maybe Text -> Addr -> IO (Maybe SourceCode)
fetchContractSource apiKey addr = do
url <- parseRequest $ "https://api.etherscan.io/api?"
<> "module=contract"
<> "&action=getsourcecode"
<> "&address=" <> show addr
<> maybe "" ("&apikey=" <>) apiKey
<> T.unpack (maybe "" ("&apikey=" <>) apiKey)
try url (5 :: Int)
where
try url n = do
Expand Down
2 changes: 2 additions & 0 deletions src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,14 @@ overrideConfig :: EConfig -> Options -> IO EConfig
overrideConfig config Options{..} = do
envRpcUrl <- Onchain.rpcUrlEnv
envRpcBlock <- Onchain.rpcBlockEnv
envEtherscanApiKey <- Onchain.etherscanApiKey
pure $
config { solConf = overrideSolConf config.solConf
, campaignConf = overrideCampaignConf config.campaignConf
, uiConf = overrideUiConf config.uiConf
, rpcUrl = cliRpcUrl <|> envRpcUrl <|> config.rpcUrl
, rpcBlock = cliRpcBlock <|> envRpcBlock <|> config.rpcBlock
, etherscanApiKey = envEtherscanApiKey <|> config.etherscanApiKey
}
& overrideFormat
where
Expand Down
2 changes: 2 additions & 0 deletions tests/solidity/basic/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ maxValue: 100000000000000000000 # 100 eth
rpcUrl: null
# block number to use when fetching over RPC
rpcBlock: null
# Etherscan API key
etherscanApiKey: null
# number of workers
workers: 1
# events server port
Expand Down

0 comments on commit 7ff9173

Please sign in to comment.