Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configuring Etherscan API key via config file #1227

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 @@ -216,12 +216,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
Loading